diff options
-rw-r--r-- | bench/Xfer4fBench.cpp | 73 | ||||
-rw-r--r-- | gm/xfer4f.cpp | 83 | ||||
-rw-r--r-- | gyp/core.gypi | 1 | ||||
-rw-r--r-- | include/core/SkColor.h | 16 | ||||
-rw-r--r-- | src/core/SkColor.cpp | 5 | ||||
-rw-r--r-- | src/core/SkPM4fPriv.h | 121 | ||||
-rw-r--r-- | src/core/SkXfer4f.cpp | 134 | ||||
-rw-r--r-- | src/core/SkXfer4f.h | 24 |
8 files changed, 452 insertions, 5 deletions
diff --git a/bench/Xfer4fBench.cpp b/bench/Xfer4fBench.cpp new file mode 100644 index 0000000000..2c281695ef --- /dev/null +++ b/bench/Xfer4fBench.cpp @@ -0,0 +1,73 @@ + +/* + * 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 "Benchmark.h" +#include "SkString.h" +#include "SkXfer4f.h" + +#define INNER_LOOPS 100 + +// Benchmark that draws non-AA rects or AA text with an SkXfermode::Mode. +class Xfer4fBench : public Benchmark { +public: + Xfer4fBench(SkXfermode::Mode mode, const char name[], bool doN, uint32_t flags) : fDoN(doN) { + fProc1 = SkPM4fXfer1ProcFactory(mode, flags); + fProcN = SkPM4fXferNProcFactory(mode, flags); + fName.printf("xfer4f_%s_%c_%s_%s", name, fDoN ? 'N' : '1', + (flags & kSrcIsOpaque_SkXfer4fFlag) ? "opaque" : "alpha", + (flags & kDstIsSRGB_SkXfer4fFlag) ? "srgb" : "linear"); + + SkPM4f c; + c.fVec[0] = 1; c.fVec[1] = 1; c.fVec[2] = 1; c.fVec[3] = 1; + for (int i = 0; i < N; ++i) { + fSrc[i] = c; + fDst[i] = 0; + } + } + +protected: + bool isSuitableFor(Backend backend) override { return backend == kNonRendering_Backend; } + + const char* onGetName() override { return fName.c_str(); } + + void onDraw(int loops, SkCanvas*) override { + for (int i = 0; i < loops; ++i) { + for (int j = 0; j < INNER_LOOPS; ++j) { + if (fDoN) { + fProcN(fDst, fSrc, N); + } else { + fProc1(fDst, fSrc[0], N); + } + } + } + } + +private: + SkString fName; + SkPM4fXfer1Proc fProc1; + SkPM4fXferNProc fProcN; + bool fDoN; + + enum { + N = 1000, + }; + SkPM4f fSrc[N]; + SkPMColor fDst[N]; + + typedef Benchmark INHERITED; +}; + +DEF_BENCH( return new Xfer4fBench(SkXfermode::kSrcOver_Mode, "srcover", false, kDstIsSRGB_SkXfer4fFlag); ) +DEF_BENCH( return new Xfer4fBench(SkXfermode::kSrcOver_Mode, "srcover", false, 0); ) +DEF_BENCH( return new Xfer4fBench(SkXfermode::kSrcOver_Mode, "srcover", false, kDstIsSRGB_SkXfer4fFlag | kSrcIsOpaque_SkXfer4fFlag); ) +DEF_BENCH( return new Xfer4fBench(SkXfermode::kSrcOver_Mode, "srcover", false, kSrcIsOpaque_SkXfer4fFlag); ) + +DEF_BENCH( return new Xfer4fBench(SkXfermode::kSrcOver_Mode, "srcover", true, kDstIsSRGB_SkXfer4fFlag); ) +DEF_BENCH( return new Xfer4fBench(SkXfermode::kSrcOver_Mode, "srcover", true, 0); ) +DEF_BENCH( return new Xfer4fBench(SkXfermode::kSrcOver_Mode, "srcover", true, kDstIsSRGB_SkXfer4fFlag | kSrcIsOpaque_SkXfer4fFlag); ) +DEF_BENCH( return new Xfer4fBench(SkXfermode::kSrcOver_Mode, "srcover", true, kSrcIsOpaque_SkXfer4fFlag); ) diff --git a/gm/xfer4f.cpp b/gm/xfer4f.cpp new file mode 100644 index 0000000000..1951fdbde2 --- /dev/null +++ b/gm/xfer4f.cpp @@ -0,0 +1,83 @@ +/* + * 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 "SkCanvas.h" +#include "SkImageInfo.h" +#include "SkXfer4f.h" + +static void draw_rect(SkCanvas* canvas, const SkRect& r, SkColor c, SkColorProfileType profile) { + const SkIRect ir = r.round(); + + SkBitmap bm; + bm.allocN32Pixels(ir.width(), ir.height()); + bm.eraseColor(0xFFFFFFFF); + SkPixmap pm; + bm.peekPixels(&pm); + + uint32_t flags = 0; + if (SkColorGetA(c) == 0xFF) { + flags |= kSrcIsOpaque_SkXfer4fFlag; + } + if (kSRGB_SkColorProfileType == profile) { + flags |= kDstIsSRGB_SkXfer4fFlag; + } + + const SkPM4f src = SkPM4f::FromPMColor(SkPreMultiplyColor(c)); + auto proc1 = SkPM4fXfer1ProcFactory(SkXfermode::kSrcOver_Mode, flags); + for (int y = 0; y < ir.height()/2; ++y) { + proc1(pm.writable_addr32(0, y), src, ir.width()); + } + + SkPM4f srcRow[1000]; + for (int i = 0; i < ir.width(); ++i) { + srcRow[i] = src; + } + auto procN = SkPM4fXferNProcFactory(SkXfermode::kSrcOver_Mode, flags); + // +1 to skip a row, so we can see the boundary between proc1 and procN + for (int y = ir.height()/2 + 1; y < ir.height(); ++y) { + procN(pm.writable_addr32(0, y), srcRow, ir.width()); + } + + canvas->drawBitmap(bm, r.left(), r.top(), nullptr); +} + +/* + * Test SkXfer4fProcs directly for src-over, comparing them to current SkColor blits. + */ +DEF_SIMPLE_GM(xfer4f_srcover, canvas, 580, 380) { + const SkScalar W = 50; + const SkScalar H = 100; + + const int profiles[] = { + -1, + kLinear_SkColorProfileType, + kSRGB_SkColorProfileType, + }; + const SkColor colors[] = { + SK_ColorBLACK, SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, + 0x88000000, 0x88FF0000, 0x8800FF00, 0x880000FF + }; + canvas->translate(20, 20); + + const SkRect r = SkRect::MakeWH(W, H); + for (auto profile : profiles) { + canvas->save(); + for (SkColor c : colors) { + if (profile < 0) { + SkPaint p; + p.setColor(c); + canvas->drawRect(r, p); + } else { + draw_rect(canvas, r, c, (SkColorProfileType)profile); + } + canvas->translate(W + 20, 0); + } + canvas->restore(); + canvas->translate(0, H + 20); + } +} diff --git a/gyp/core.gypi b/gyp/core.gypi index 1ecaa3ed22..b5405a40ca 100644 --- a/gyp/core.gypi +++ b/gyp/core.gypi @@ -293,6 +293,7 @@ '<(skia_src_path)/core/SkVertState.cpp', '<(skia_src_path)/core/SkWriteBuffer.cpp', '<(skia_src_path)/core/SkWriter32.cpp', + '<(skia_src_path)/core/SkXfer4f.cpp', '<(skia_src_path)/core/SkXfermode.cpp', '<(skia_src_path)/core/SkXfermode_proccoeff.h', '<(skia_src_path)/core/SkXfermodeInterpretation.cpp', diff --git a/include/core/SkColor.h b/include/core/SkColor.h index 3ebd18b7aa..099771af98 100644 --- a/include/core/SkColor.h +++ b/include/core/SkColor.h @@ -110,8 +110,7 @@ SK_API void SkRGBToHSV(U8CPU red, U8CPU green, U8CPU blue, SkScalar hsv[3]); @param color the argb color to convert. Note: the alpha component is ignored. @param hsv 3 element array which holds the resulting HSV components. */ -static inline void SkColorToHSV(SkColor color, SkScalar hsv[3]) -{ +static inline void SkColorToHSV(SkColor color, SkScalar hsv[3]) { SkRGBToHSV(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color), hsv); } @@ -134,8 +133,7 @@ SK_API SkColor SkHSVToColor(U8CPU alpha, const SkScalar hsv[3]); @param hsv 3 element array which holds the input HSV components. @return the resulting argb color */ -static inline SkColor SkHSVToColor(const SkScalar hsv[3]) -{ +static inline SkColor SkHSVToColor(const SkScalar hsv[3]) { return SkHSVToColor(0xFF, hsv); } @@ -166,11 +164,19 @@ typedef SkPMColor (*SkXfermodeProc)(SkPMColor src, SkPMColor dst); * The float values are 0...1 premultiplied */ struct SkPM4f { + enum { + A = SK_A32_SHIFT/8, + R = SK_R32_SHIFT/8, + G = SK_G32_SHIFT/8, + B = SK_B32_SHIFT/8, + }; float fVec[4]; - float a() const { return fVec[SK_A32_SHIFT/8]; } + float a() const { return fVec[3]; } static SkPM4f FromPMColor(SkPMColor); + + bool isUnit() const; }; /* diff --git a/src/core/SkColor.cpp b/src/core/SkColor.cpp index 1794cd7d90..9deac7a197 100644 --- a/src/core/SkColor.cpp +++ b/src/core/SkColor.cpp @@ -141,3 +141,8 @@ SkPM4f SkColor4f::premul() const { dst.store(pm4.fVec); return pm4; } + +bool SkPM4f::isUnit() const { + auto c4 = Sk4f::Load(fVec); + return (c4 >= Sk4f(0)).allTrue() && (c4 <= Sk4f(1)).allTrue(); +} diff --git a/src/core/SkPM4fPriv.h b/src/core/SkPM4fPriv.h new file mode 100644 index 0000000000..a4cec5c449 --- /dev/null +++ b/src/core/SkPM4fPriv.h @@ -0,0 +1,121 @@ +/* + * 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 "SkColorPriv.h" +#include "SkNx.h" + +static inline float get_alpha(const Sk4f& f4) { + return f4.kth<SkPM4f::A>(); +} + +static inline Sk4f set_alpha(const Sk4f& f4, float alpha) { + static_assert(3 == SkPM4f::A, ""); + return Sk4f(f4.kth<0>(), f4.kth<1>(), f4.kth<2>(), alpha); +} + +static inline uint32_t to_4b(const Sk4f& f4) { + uint32_t b4; + SkNx_cast<uint8_t>(f4).store((uint8_t*)&b4); + return b4; +} + +static inline Sk4f to_4f(uint32_t b4) { + return SkNx_cast<float>(Sk4b::Load((const uint8_t*)&b4)); +} + +static inline Sk4f s2l(const Sk4f& s4) { + return set_alpha(s4 * s4, get_alpha(s4)); +} + +static inline Sk4f l2s(const Sk4f& l4) { + return set_alpha(l4.rsqrt1() * l4, get_alpha(l4)); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +static inline Sk4f Sk4f_fromL32(uint32_t src) { + return to_4f(src) * Sk4f(1.0f/255); +} + +static inline Sk4f Sk4f_fromS32(uint32_t src) { + return s2l(to_4f(src) * Sk4f(1.0f/255)); +} + +static inline uint32_t Sk4f_toL32(const Sk4f& x4) { + return to_4b(x4 * Sk4f(255) + Sk4f(0.5f)); +} + +static inline uint32_t Sk4f_toS32(const Sk4f& x4) { + return to_4b(l2s(x4) * Sk4f(255) + Sk4f(0.5f)); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +static Sk4f unit_to_l255_round(const SkPM4f& pm4) { + return Sk4f::Load(pm4.fVec) * Sk4f(255) + Sk4f(0.5f); +} + +static Sk4f unit_to_s255_round(const SkPM4f& pm4) { + return l2s(Sk4f::Load(pm4.fVec)) * Sk4f(255) + Sk4f(0.5f); +} + +static inline void SkPM4f_l32_src_mode(SkPMColor dst[], const SkPM4f src[], int count) { + for (int i = 0; i < (count >> 2); ++i) { + SkASSERT(src[0].isUnit()); + SkASSERT(src[1].isUnit()); + SkASSERT(src[2].isUnit()); + SkASSERT(src[3].isUnit()); + Sk4f_ToBytes((uint8_t*)dst, + unit_to_l255_round(src[0]), unit_to_l255_round(src[1]), + unit_to_l255_round(src[2]), unit_to_l255_round(src[3])); + src += 4; + dst += 4; + } + count &= 3; + for (int i = 0; i < count; ++i) { + SkASSERT(src[i].isUnit()); + SkNx_cast<uint8_t>(unit_to_l255_round(src[i])).store((uint8_t*)&dst[i]); + } +} + +static inline void SkPM4f_l32_srcover_mode(SkPMColor dst[], const SkPM4f src[], int count) { + for (int i = 0; i < count; ++i) { + SkASSERT(src[i].isUnit()); + Sk4f s4 = Sk4f::Load(src[i].fVec); + Sk4f d4 = Sk4f_fromL32(dst[i]); + dst[i] = Sk4f_toL32(s4 + d4 * Sk4f(1 - get_alpha(s4))); + } +} + +static inline void SkPM4f_s32_src_mode(SkPMColor dst[], const SkPM4f src[], int count) { + for (int i = 0; i < (count >> 2); ++i) { + SkASSERT(src[0].isUnit()); + SkASSERT(src[1].isUnit()); + SkASSERT(src[2].isUnit()); + SkASSERT(src[3].isUnit()); + Sk4f_ToBytes((uint8_t*)dst, + unit_to_s255_round(src[0]), unit_to_s255_round(src[1]), + unit_to_s255_round(src[2]), unit_to_s255_round(src[3])); + src += 4; + dst += 4; + } + count &= 3; + for (int i = 0; i < count; ++i) { + SkASSERT(src[i].isUnit()); + SkNx_cast<uint8_t>(unit_to_s255_round(src[i])).store((uint8_t*)&dst[i]); + } +} + +static inline void SkPM4f_s32_srcover_mode(SkPMColor dst[], const SkPM4f src[], int count) { + for (int i = 0; i < count; ++i) { + SkASSERT(src[i].isUnit()); + Sk4f s4 = Sk4f::Load(src[i].fVec); + Sk4f d4 = Sk4f_fromS32(dst[i]); + dst[i] = Sk4f_toS32(s4 + d4 * Sk4f(1 - get_alpha(s4))); + } +} + diff --git a/src/core/SkXfer4f.cpp b/src/core/SkXfer4f.cpp new file mode 100644 index 0000000000..b177e49b18 --- /dev/null +++ b/src/core/SkXfer4f.cpp @@ -0,0 +1,134 @@ +/* + * 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 "SkXfer4f.h" +#include "SkPM4fPriv.h" +#include "SkUtils.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +void CLEAR_pm41p(uint32_t dst[], const SkPM4f& src, int count) { + sk_bzero(dst, count * sizeof(uint32_t)); +} + +void CLEAR_pm4np(uint32_t dst[], const SkPM4f src[], int count) { + sk_bzero(dst, count * sizeof(uint32_t)); +} + +////////// + +template <bool isSRGB> void SRC_pm41p(uint32_t dst[], const SkPM4f& src, int count) { + uint32_t res; + if (isSRGB) { + res = Sk4f_toS32(Sk4f::Load(src.fVec)); + } else { + res = Sk4f_toL32(Sk4f::Load(src.fVec)); + } + sk_memset32(dst, res, count); +} + +template <bool isSRGB> void SRC_pm4np(uint32_t dst[], const SkPM4f src[], int count) { + if (isSRGB) { + SkPM4f_s32_src_mode(dst, src, count); + } else { + SkPM4f_l32_src_mode(dst, src, count); + } +} + +////////// + +void DST_pm41p(uint32_t dst[], const SkPM4f& src, int count) {} +void DST_pm4np(uint32_t dst[], const SkPM4f src[], int count) {} + +////////// + +template <bool isSRGB> void SRCOVER_pm41p(uint32_t dst[], const SkPM4f& src, int count) { + SkASSERT(src.isUnit()); + Sk4f s4 = Sk4f::Load(src.fVec); + Sk4f scale(1 - s4.kth<SkPM4f::A>()); + + if (!isSRGB) { + s4 = s4 * Sk4f(255); + } + + for (int i = 0; i < count; ++i) { + if (isSRGB) { + Sk4f d4 = Sk4f_fromS32(dst[i]); + dst[i] = Sk4f_toS32(s4 + d4 * scale); + } else { + Sk4f d4 = to_4f(dst[i]); + dst[i] = to_4b(s4 + d4 * scale + Sk4f(0.5f)); + } + } +} + +template <bool isSRGB> void SRCOVER_pm4np(uint32_t dst[], const SkPM4f src[], int count) { + if (isSRGB) { + SkPM4f_s32_srcover_mode(dst, src, count); + } else { + SkPM4f_l32_srcover_mode(dst, src, count); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +struct Pair { + SkPM4fXfer1Proc fProc1; + SkPM4fXferNProc fProcN; +}; + +const Pair gClearPairs[] = { + { CLEAR_pm41p, CLEAR_pm4np }, + { CLEAR_pm41p, CLEAR_pm4np }, + { CLEAR_pm41p, CLEAR_pm4np }, + { CLEAR_pm41p, CLEAR_pm4np }, +}; + +const Pair gSrcPairs[] = { + { SRC_pm41p<false>, SRC_pm4np<false> }, // linear [alpha ignored] + { SRC_pm41p<false>, SRC_pm4np<false> }, // linear [opaque ignored] + { SRC_pm41p<true>, SRC_pm4np<true> }, // srgb [alpha ignored] + { SRC_pm41p<true>, SRC_pm4np<true> }, // srgb [opaque ignored] +}; + +const Pair gDstPairs[] = { + { DST_pm41p, DST_pm4np }, + { DST_pm41p, DST_pm4np }, + { DST_pm41p, DST_pm4np }, + { DST_pm41p, DST_pm4np }, +}; + +const Pair gSrcOverPairs[] = { + { SRCOVER_pm41p<false>, SRCOVER_pm4np<false> }, // linear alpha + { SRC_pm41p<false>, SRC_pm4np<false> }, // linear opaque + { SRCOVER_pm41p<true>, SRCOVER_pm4np<true> }, // srgb alpha + { SRC_pm41p<true>, SRC_pm4np<true> }, // srgb opaque +}; + +static const Pair* find_pair(SkXfermode::Mode mode, uint32_t flags) { + SkASSERT(0 == (flags & ~3)); + const Pair* pairs = nullptr; + + switch (mode) { + case SkXfermode::kClear_Mode: pairs = gClearPairs; + case SkXfermode::kSrc_Mode: pairs = gSrcPairs; break; + case SkXfermode::kDst_Mode: pairs = gDstPairs; + case SkXfermode::kSrcOver_Mode: pairs = gSrcOverPairs; break; + default: return nullptr; + } + return &pairs[flags & 3]; +} + +SkPM4fXfer1Proc SkPM4fXfer1ProcFactory(SkXfermode::Mode mode, uint32_t flags) { + const Pair* pair = find_pair(mode, flags); + return pair ? pair->fProc1 : nullptr; +} + +SkPM4fXferNProc SkPM4fXferNProcFactory(SkXfermode::Mode mode, uint32_t flags) { + const Pair* pair = find_pair(mode, flags); + return pair ? pair->fProcN : nullptr; +} diff --git a/src/core/SkXfer4f.h b/src/core/SkXfer4f.h new file mode 100644 index 0000000000..fd6657f1c2 --- /dev/null +++ b/src/core/SkXfer4f.h @@ -0,0 +1,24 @@ +/* + * 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 SkXfermodePriv_DEFINED +#define SkXfermodePriv_DEFINED + +#include "SkXfermode.h" + +enum SkXfef4fFlags { + kSrcIsOpaque_SkXfer4fFlag = 1 << 0, + kDstIsSRGB_SkXfer4fFlag = 1 << 1, +}; + +typedef void (*SkPM4fXfer1Proc)(uint32_t dst[], const SkPM4f& src, int count); +typedef void (*SkPM4fXferNProc)(uint32_t dst[], const SkPM4f src[], int count); + +SkPM4fXfer1Proc SkPM4fXfer1ProcFactory(SkXfermode::Mode, uint32_t flags); +SkPM4fXferNProc SkPM4fXferNProcFactory(SkXfermode::Mode, uint32_t flags); + +#endif |