diff options
-rw-r--r-- | include/core/SkXfermode.h | 10 | ||||
-rw-r--r-- | src/core/SkBlitter_PM4f.cpp | 73 | ||||
-rw-r--r-- | src/core/SkXfermode4f.cpp | 95 |
3 files changed, 175 insertions, 3 deletions
diff --git a/include/core/SkXfermode.h b/include/core/SkXfermode.h index 21d77f8747..3b0f49d22e 100644 --- a/include/core/SkXfermode.h +++ b/include/core/SkXfermode.h @@ -248,6 +248,16 @@ public: static U64Proc1 GetU64Proc1(Mode, uint32_t flags); static U64ProcN GetU64ProcN(Mode, uint32_t flags); + enum LCDFlags { + kSrcIsOpaque_LCDFlag = 1 << 0, // else src(s) may have alpha < 1 + kSrcIsSingle_LCDFlag = 1 << 1, // else src[count] + kDstIsLinearInt_LCDFlag = 1 << 2, // else srgb/half-float + }; + typedef void (*LCD32Proc)(uint32_t* dst, const SkPM4f* src, int count, const uint16_t lcd[]); + typedef void (*LCD64Proc)(uint64_t* dst, const SkPM4f* src, int count, const uint16_t lcd[]); + static LCD32Proc GetLCD32Proc(uint32_t flags); + static LCD64Proc GetLCD64Proc(uint32_t) { return nullptr; } + protected: SkXfermode() {} /** The default implementation of xfer32/xfer16/xferA8 in turn call this diff --git a/src/core/SkBlitter_PM4f.cpp b/src/core/SkBlitter_PM4f.cpp index 01fd704254..cec2361c06 100644 --- a/src/core/SkBlitter_PM4f.cpp +++ b/src/core/SkBlitter_PM4f.cpp @@ -80,8 +80,31 @@ public: } } + void blitLCDMask(const SkMask& mask, const SkIRect& clip) { + auto proc = fState.getLCDProc(SkXfermode::kSrcIsSingle_LCDFlag); + + const int x = clip.fLeft; + const int width = clip.width(); + const int y = clip.fTop; + const int height = clip.height(); + + typename State::DstType* device = State::WritableAddr(fDevice, x, y); + const size_t dstRB = fDevice.rowBytes(); + const uint16_t* maskRow = (const uint16_t*)mask.getAddr(x, y); + const size_t maskRB = mask.fRowBytes; + + for (int i = 0; i < height; ++i) { + proc(device, &fState.fPM4f, width, maskRow); + device = (typename State::DstType*)((char*)device + dstRB); + maskRow = (const uint16_t*)((const char*)maskRow + maskRB); + } + } + void blitMask(const SkMask& mask, const SkIRect& clip) override { - // we only handle kA8 + if (SkMask::kLCD16_Format == mask.fFormat) { + this->blitLCDMask(mask, clip); + return; + } if (SkMask::kA8_Format != mask.fFormat) { this->INHERITED::blitMask(mask, clip); return; @@ -190,8 +213,36 @@ public: } } + void blitLCDMask(const SkMask& mask, const SkIRect& clip) { + auto proc = fState.getLCDProc(0); + + const int x = clip.fLeft; + const int width = clip.width(); + int y = clip.fTop; + + typename State::DstType* device = State::WritableAddr(fDevice, x, y); + const size_t deviceRB = fDevice.rowBytes(); + const uint16_t* maskRow = (const uint16_t*)mask.getAddr(x, y); + const size_t maskRB = mask.fRowBytes; + + if (fConstInY) { + fShaderContext->shadeSpan4f(x, y, fState.fBuffer, width); + } + for (; y < clip.fBottom; ++y) { + if (!fConstInY) { + fShaderContext->shadeSpan4f(x, y, fState.fBuffer, width); + } + proc(device, fState.fBuffer, width, maskRow); + device = (typename State::DstType*)((char*)device + deviceRB); + maskRow = (const uint16_t*)((const char*)maskRow + maskRB); + } + } + void blitMask(const SkMask& mask, const SkIRect& clip) override { - // we only handle kA8 + if (SkMask::kLCD16_Format == mask.fFormat) { + this->blitLCDMask(mask, clip); + return; + } if (SkMask::kA8_Format != mask.fFormat) { this->INHERITED::blitMask(mask, clip); return; @@ -271,7 +322,15 @@ struct State32 : SkXfermode::PM4fState { SkSafeUnref(fXfer); delete[] fBuffer; } - + + SkXfermode::LCD32Proc getLCDProc(uint32_t oneOrManyFlag) const { + uint32_t flags = fFlags & 1; + if (!(fFlags & SkXfermode::kDstIsSRGB_PM4fFlag)) { + flags |= SkXfermode::kDstIsLinearInt_LCDFlag; + } + return SkXfermode::GetLCD32Proc(flags | oneOrManyFlag); + } + static DstType* WritableAddr(const SkPixmap& device, int x, int y) { return device.writable_addr32(x, y); } @@ -315,6 +374,14 @@ struct State64 : SkXfermode::U64State { delete[] fBuffer; } + SkXfermode::LCD64Proc getLCDProc(uint32_t oneOrManyFlag) const { + uint32_t flags = fFlags & 1; + if (!(fFlags & SkXfermode::kDstIsFloat16_U64Flag)) { + flags |= SkXfermode::kDstIsLinearInt_LCDFlag; + } + return SkXfermode::GetLCD64Proc(flags | oneOrManyFlag); + } + static DstType* WritableAddr(const SkPixmap& device, int x, int y) { return device.writable_addr64(x, y); } diff --git a/src/core/SkXfermode4f.cpp b/src/core/SkXfermode4f.cpp index 9aba0da04f..bf30e92ae7 100644 --- a/src/core/SkXfermode4f.cpp +++ b/src/core/SkXfermode4f.cpp @@ -422,3 +422,98 @@ SkXfermode::PM4fProcN SkXfermode::getPM4fProcN(uint32_t flags) const { Mode mode; return this->asMode(&mode) ? GetPM4fProcN(mode, flags) : xfer_pm4_proc_n; } + +/////////////////////////////////////////////////////////////////////////////////////////////////// +#include "SkColorPriv.h" + +static Sk4f lcd16_to_unit_4f(uint16_t rgb) { + Sk4i rgbi = Sk4i(SkGetPackedR16(rgb), SkGetPackedG16(rgb), SkGetPackedB16(rgb), 0); + return SkNx_cast<float>(rgbi) * Sk4f(1.0f/31, 1.0f/63, 1.0f/31, 0); +} + +template <DstType D> +void src_1_lcd(uint32_t dst[], const SkPM4f* src, int count, const uint16_t lcd[]) { + const Sk4f s4 = Sk4f::Load(src->fVec); + + if (D == kLinear_Dst) { + // operate in bias-255 space for src and dst + const Sk4f s4bias = s4 * Sk4f(255); + for (int i = 0; i < count; ++i) { + uint16_t rgb = lcd[i]; + if (0 == rgb) { + continue; + } + Sk4f d4bias = to_4f(dst[i]); + dst[i] = to_4b(lerp(s4bias, d4bias, lcd16_to_unit_4f(rgb))) | (SK_A32_MASK << SK_A32_SHIFT); + } + } else { // kSRGB + for (int i = 0; i < count; ++i) { + uint16_t rgb = lcd[i]; + if (0 == rgb) { + continue; + } + Sk4f d4 = load_dst<D>(dst[i]); + dst[i] = store_dst<D>(lerp(s4, d4, lcd16_to_unit_4f(rgb))) | (SK_A32_MASK << SK_A32_SHIFT); + } + } +} + +template <DstType D> +void src_n_lcd(uint32_t dst[], const SkPM4f src[], int count, const uint16_t lcd[]) { + for (int i = 0; i < count; ++i) { + uint16_t rgb = lcd[i]; + if (0 == rgb) { + continue; + } + Sk4f s4 = Sk4f::Load(src[i].fVec); + Sk4f d4 = load_dst<D>(dst[i]); + dst[i] = store_dst<D>(lerp(s4, d4, lcd16_to_unit_4f(rgb))) | (SK_A32_MASK << SK_A32_SHIFT); + } +} + +template <DstType D> +void srcover_1_lcd(uint32_t dst[], const SkPM4f* src, int count, const uint16_t lcd[]) { + const Sk4f s4 = Sk4f::Load(src->fVec); + Sk4f dst_scale = Sk4f(1 - get_alpha(s4)); + + for (int i = 0; i < count; ++i) { + uint16_t rgb = lcd[i]; + if (0 == rgb) { + continue; + } + Sk4f d4 = load_dst<D>(dst[i]); + Sk4f r4 = s4 + d4 * dst_scale; + r4 = lerp(r4, d4, lcd16_to_unit_4f(rgb)); + dst[i] = store_dst<D>(r4) | (SK_A32_MASK << SK_A32_SHIFT); + } +} + +template <DstType D> +void srcover_n_lcd(uint32_t dst[], const SkPM4f src[], int count, const uint16_t lcd[]) { + for (int i = 0; i < count; ++i) { + uint16_t rgb = lcd[i]; + if (0 == rgb) { + continue; + } + Sk4f s4 = Sk4f::Load(src[i].fVec); + Sk4f dst_scale = Sk4f(1 - get_alpha(s4)); + Sk4f d4 = load_dst<D>(dst[i]); + Sk4f r4 = s4 + d4 * dst_scale; + r4 = lerp(r4, d4, lcd16_to_unit_4f(rgb)); + dst[i] = store_dst<D>(r4) | (SK_A32_MASK << SK_A32_SHIFT); + } +} + +SkXfermode::LCD32Proc SkXfermode::GetLCD32Proc(uint32_t flags) { + SkASSERT((flags & ~7) == 0); + flags &= 7; + + const LCD32Proc procs[] = { + srcover_n_lcd<kSRGB_Dst>, src_n_lcd<kSRGB_Dst>, + srcover_1_lcd<kSRGB_Dst>, src_1_lcd<kSRGB_Dst>, + + srcover_n_lcd<kLinear_Dst>, src_n_lcd<kLinear_Dst>, + srcover_1_lcd<kLinear_Dst>, src_1_lcd<kLinear_Dst>, + }; + return procs[flags]; +} |