From 2a1d401bf2d500bab225b78a20cb6f16b2c67efb Mon Sep 17 00:00:00 2001 From: robertphillips Date: Fri, 29 Jan 2016 08:44:17 -0800 Subject: [PATCH] Move SkAvoidXfermode over from Android GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1649503002 Review URL: https://codereview.chromium.org/1649503002 --- gm/avoidxfermode.cpp | 99 +++++++++++++++ gyp/effects.gypi | 2 + include/effects/SkAvoidXfermode.h | 70 +++++++++++ src/effects/SkAvoidXfermode.cpp | 177 +++++++++++++++++++++++++++ src/ports/SkGlobalInitialization_default.cpp | 2 + 5 files changed, 350 insertions(+) create mode 100644 gm/avoidxfermode.cpp create mode 100644 include/effects/SkAvoidXfermode.h create mode 100644 src/effects/SkAvoidXfermode.cpp diff --git a/gm/avoidxfermode.cpp b/gm/avoidxfermode.cpp new file mode 100644 index 0000000..0d681cb --- /dev/null +++ b/gm/avoidxfermode.cpp @@ -0,0 +1,99 @@ +/* + * 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 "gm.h" +#include "Resources.h" + +#include "SkImageDecoder.h" +#include "SkAvoidXfermode.h" +#include "SkStream.h" + +class AvoidXfermodeGM : public skiagm::GM { +public: + AvoidXfermodeGM() { } + +protected: + SkString onShortName() override { + return SkString("avoidxfermode"); + } + + SkISize onISize() override { return SkISize::Make(128, 128); } + + void onOnceBeforeDraw() override { + SkImageDecoder* codec = nullptr; + SkString resourcePath = GetResourcePath("color_wheel.png"); + SkFILEStream stream(resourcePath.c_str()); + if (stream.isValid()) { + codec = SkImageDecoder::Factory(&stream); + } + if (codec) { + stream.rewind(); + codec->decode(&stream, &fBM, kN32_SkColorType, SkImageDecoder::kDecodePixels_Mode); + delete codec; + } else { + fBM.allocN32Pixels(1, 1); + fBM.eraseARGB(255, 255, 0 , 0); // red == bad + } + } + + void onDraw(SkCanvas* canvas) override { + canvas->drawBitmap(fBM, 0, 0); + + SkRect r = SkRect::MakeIWH(64, 64); + + // UL corner: replace white with black with a tight tolerance + SkPaint p1; + p1.setColor(SK_ColorBLACK); + p1.setXfermode(SkAvoidXfermode::Create(SK_ColorWHITE, + 5, + SkAvoidXfermode::kTargetColor_Mode))->unref(); + + canvas->drawRect(r, p1); + + r.offsetTo(64, 0.0f); + + // UR corner: draw black everywhere except white with a tight tolerance + SkPaint p2; + p2.setColor(SK_ColorBLACK); + p2.setXfermode(SkAvoidXfermode::Create(SK_ColorWHITE, + 250, + SkAvoidXfermode::kAvoidColor_Mode))->unref(); + + canvas->drawRect(r, p2); + + r.offsetTo(0.0f, 64); + + // LL corner: replace red with blue with a loose tolerance + SkPaint p3; + p3.setColor(SK_ColorBLUE); + p3.setXfermode(SkAvoidXfermode::Create(SK_ColorRED, + 250, + SkAvoidXfermode::kTargetColor_Mode))->unref(); + + canvas->drawRect(r, p3); + + r.offsetTo(64, 64); + + // LR corner: draw black everywhere except red with a loose tolerance + SkPaint p4; + p4.setColor(SK_ColorBLACK); + p4.setXfermode(SkAvoidXfermode::Create(SK_ColorRED, + 5, + SkAvoidXfermode::kAvoidColor_Mode))->unref(); + + canvas->drawRect(r, p4); + } + +private: + SkBitmap fBM; + + typedef GM INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +DEF_GM(return new AvoidXfermodeGM;) diff --git a/gyp/effects.gypi b/gyp/effects.gypi index af09357..cee399d 100644 --- a/gyp/effects.gypi +++ b/gyp/effects.gypi @@ -21,6 +21,7 @@ '<(skia_src_path)/effects/SkArithmeticMode.cpp', '<(skia_src_path)/effects/SkArithmeticMode_gpu.cpp', '<(skia_src_path)/effects/SkArithmeticMode_gpu.h', + '<(skia_src_path)/effects/SkAvoidXfermode.cpp', '<(skia_src_path)/effects/SkBlurDrawLooper.cpp', '<(skia_src_path)/effects/SkBlurMask.cpp', '<(skia_src_path)/effects/SkBlurMask.h', @@ -84,6 +85,7 @@ '<(skia_include_path)/effects/Sk2DPathEffect.h', '<(skia_include_path)/effects/SkAlphaThresholdFilter.h', '<(skia_include_path)/effects/SkArithmeticMode.h', + '<(skia_include_path)/effects/SkAvoidXfermode.h', '<(skia_include_path)/effects/SkBlurDrawLooper.h', '<(skia_include_path)/effects/SkBlurImageFilter.h', '<(skia_include_path)/effects/SkBlurMaskFilter.h', diff --git a/include/effects/SkAvoidXfermode.h b/include/effects/SkAvoidXfermode.h new file mode 100644 index 0000000..bcd42a5 --- /dev/null +++ b/include/effects/SkAvoidXfermode.h @@ -0,0 +1,70 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkAvoidXfermode_DEFINED +#define SkAvoidXfermode_DEFINED + +#include "SkColor.h" +#include "SkTypes.h" +#include "SkXfermode.h" + +/** \class AvoidXfermode + + This xfermode will draw the src everywhere except on top of the specified + color. +*/ +class SkAvoidXfermode : public SkXfermode { +public: + enum Mode { + kAvoidColor_Mode, //!< draw everywhere except on the opColor + kTargetColor_Mode //!< draw only on top of the opColor + }; + + /** This xfermode draws, or doesn't draw, based on the destination's + distance from an op-color. + + There are two modes, and each mode interprets a tolerance value. + + Avoid: In this mode, drawing is allowed only on destination pixels that + are different from the op-color. + Tolerance near 0: avoid any colors even remotely similar to the op-color + Tolerance near 255: avoid only colors nearly identical to the op-color + + Target: In this mode, drawing only occurs on destination pixels that + are similar to the op-color + Tolerance near 0: draw only on colors that are nearly identical to the op-color + Tolerance near 255: draw on any colors even remotely similar to the op-color + */ + static SkAvoidXfermode* Create(SkColor opColor, U8CPU tolerance, Mode mode) { + return new SkAvoidXfermode(opColor, tolerance, mode); + } + + // overrides from SkXfermode + void xfer32(SkPMColor dst[], const SkPMColor src[], int count, + const SkAlpha aa[]) const override; + void xfer16(uint16_t dst[], const SkPMColor src[], int count, + const SkAlpha aa[]) const override; + void xferA8(SkAlpha dst[], const SkPMColor src[], int count, + const SkAlpha aa[]) const override; + + SK_TO_STRING_OVERRIDE() + SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(AvoidXfermode) + +protected: + SkAvoidXfermode(SkColor opColor, U8CPU tolerance, Mode mode); + void flatten(SkWriteBuffer&) const override; + +private: + SkColor fOpColor; + uint32_t fDistMul; // x.14 cached from fTolerance + uint8_t fTolerance; + Mode fMode; + + typedef SkXfermode INHERITED; +}; + +#endif diff --git a/src/effects/SkAvoidXfermode.cpp b/src/effects/SkAvoidXfermode.cpp new file mode 100644 index 0000000..d0aa44f --- /dev/null +++ b/src/effects/SkAvoidXfermode.cpp @@ -0,0 +1,177 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkAvoidXfermode.h" +#include "SkColorPriv.h" +#include "SkReadBuffer.h" +#include "SkWriteBuffer.h" +#include "SkString.h" + +SkAvoidXfermode::SkAvoidXfermode(SkColor opColor, U8CPU tolerance, Mode mode) { + if (tolerance > 255) { + tolerance = 255; + } + fTolerance = SkToU8(tolerance); + fOpColor = opColor; + fDistMul = (256 << 14) / (tolerance + 1); + fMode = mode; +} + +SkFlattenable* SkAvoidXfermode::CreateProc(SkReadBuffer& buffer) { + const SkColor color = buffer.readColor(); + const unsigned tolerance = buffer.readUInt(); + const unsigned mode = buffer.readUInt(); + return Create(color, tolerance, (Mode)mode); +} + +void SkAvoidXfermode::flatten(SkWriteBuffer& buffer) const { + buffer.writeColor(fOpColor); + buffer.writeUInt(fTolerance); + buffer.writeUInt(fMode); +} + +// returns 0..31 +static unsigned color_dist16(uint16_t c, unsigned r, unsigned g, unsigned b) { + SkASSERT(r <= SK_R16_MASK); + SkASSERT(g <= SK_G16_MASK); + SkASSERT(b <= SK_B16_MASK); + + unsigned dr = SkAbs32(SkGetPackedR16(c) - r); + unsigned dg = SkAbs32(SkGetPackedG16(c) - g) >> (SK_G16_BITS - SK_R16_BITS); + unsigned db = SkAbs32(SkGetPackedB16(c) - b); + + return SkMax32(dr, SkMax32(dg, db)); +} + +// returns 0..255 +static unsigned color_dist32(SkPMColor c, U8CPU r, U8CPU g, U8CPU b) { + SkASSERT(r <= 0xFF); + SkASSERT(g <= 0xFF); + SkASSERT(b <= 0xFF); + + unsigned dr = SkAbs32(SkGetPackedR32(c) - r); + unsigned dg = SkAbs32(SkGetPackedG32(c) - g); + unsigned db = SkAbs32(SkGetPackedB32(c) - b); + + return SkMax32(dr, SkMax32(dg, db)); +} + +static int scale_dist_14(int dist, uint32_t mul, uint32_t sub) { + int tmp = dist * mul - sub; + int result = (tmp + (1 << 13)) >> 14; + + return result; +} + +static inline unsigned Accurate255To256(unsigned x) { + return x + (x >> 7); +} + +void SkAvoidXfermode::xfer32(SkPMColor dst[], const SkPMColor src[], int count, + const SkAlpha aa[]) const { + unsigned opR = SkColorGetR(fOpColor); + unsigned opG = SkColorGetG(fOpColor); + unsigned opB = SkColorGetB(fOpColor); + uint32_t mul = fDistMul; + uint32_t sub = (fDistMul - (1 << 14)) << 8; + + int MAX, mask; + + if (kTargetColor_Mode == fMode) { + mask = -1; + MAX = 255; + } else { + mask = 0; + MAX = 0; + } + + for (int i = 0; i < count; i++) { + int d = color_dist32(dst[i], opR, opG, opB); + // now reverse d if we need to + d = MAX + (d ^ mask) - mask; + SkASSERT((unsigned)d <= 255); + d = Accurate255To256(d); + + d = scale_dist_14(d, mul, sub); + SkASSERT(d <= 256); + + if (d > 0) { + if (aa) { + d = SkAlphaMul(d, Accurate255To256(*aa++)); + if (0 == d) { + continue; + } + } + dst[i] = SkFourByteInterp256(src[i], dst[i], d); + } + } +} + +static inline U16CPU SkBlend3216(SkPMColor src, U16CPU dst, unsigned scale) { + SkASSERT(scale <= 32); + scale <<= 3; + + return SkPackRGB16(SkAlphaBlend(SkPacked32ToR16(src), SkGetPackedR16(dst), scale), + SkAlphaBlend(SkPacked32ToG16(src), SkGetPackedG16(dst), scale), + SkAlphaBlend(SkPacked32ToB16(src), SkGetPackedB16(dst), scale)); +} + +void SkAvoidXfermode::xfer16(uint16_t dst[], const SkPMColor src[], int count, + const SkAlpha aa[]) const { + unsigned opR = SkColorGetR(fOpColor) >> (8 - SK_R16_BITS); + unsigned opG = SkColorGetG(fOpColor) >> (8 - SK_G16_BITS); + unsigned opB = SkColorGetB(fOpColor) >> (8 - SK_R16_BITS); + uint32_t mul = fDistMul; + uint32_t sub = (fDistMul - (1 << 14)) << SK_R16_BITS; + + int MAX, mask; + + if (kTargetColor_Mode == fMode) { + mask = -1; + MAX = 31; + } else { + mask = 0; + MAX = 0; + } + + for (int i = 0; i < count; i++) { + int d = color_dist16(dst[i], opR, opG, opB); + // now reverse d if we need to + d = MAX + (d ^ mask) - mask; + SkASSERT((unsigned)d <= 31); + // convert from 0..31 to 0..32 + d += d >> 4; + d = scale_dist_14(d, mul, sub); + SkASSERT(d <= 32); + + if (d > 0) { + if (aa) { + d = SkAlphaMul(d, Accurate255To256(*aa++)); + if (0 == d) { + continue; + } + } + dst[i] = SkBlend3216(src[i], dst[i], d); + } + } +} + +void SkAvoidXfermode::xferA8(SkAlpha dst[], const SkPMColor src[], int count, + const SkAlpha aa[]) const { +} + +#ifndef SK_IGNORE_TO_STRING +void SkAvoidXfermode::toString(SkString* str) const { + str->append("AvoidXfermode: opColor: "); + str->appendHex(fOpColor); + str->appendf("distMul: %d ", fDistMul); + + static const char* gModeStrings[] = { "Avoid", "Target" }; + + str->appendf("mode: %s", gModeStrings[fMode]); +} +#endif diff --git a/src/ports/SkGlobalInitialization_default.cpp b/src/ports/SkGlobalInitialization_default.cpp index de50527..4c06817 100644 --- a/src/ports/SkGlobalInitialization_default.cpp +++ b/src/ports/SkGlobalInitialization_default.cpp @@ -10,6 +10,7 @@ #include "SkAlphaThresholdFilter.h" #include "SkArithmeticMode.h" #include "SkArcToPathEffect.h" +#include "SkAvoidXfermode.h" #include "SkBitmapSourceDeserializer.h" #include "SkBlurDrawLooper.h" #include "SkBlurImageFilter.h" @@ -82,6 +83,7 @@ void SkFlattenable::PrivateInitializer::InitEffects() { // Xfermode SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPixelXorXfermode) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkAvoidXfermode) // PathEffect SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkArcToPathEffect) -- 2.7.4