experiments to speed up drawing 32bit images into 565 New (legacy style) blitters only coded for shaders (and very restricted blendmodes) (cherry picked from commit ef8ce2858b7692c9ec9874c43656e603f304c760) Bug: skia: Change-Id: Id57ee84f1c1028b79c7ccca8919480c68ac4b9c6 Reviewed-on: https://skia-review.googlesource.com/56400 Commit-Queue: Derek Sollenberger <djsollen@google.com> Reviewed-by: Derek Sollenberger <djsollen@google.com>
diff --git a/gn/core.gni b/gn/core.gni index 39a80fe..75d3245 100644 --- a/gn/core.gni +++ b/gn/core.gni
@@ -56,6 +56,7 @@ "$_src/core/SkBlitter.cpp", "$_src/core/SkBlitter_A8.cpp", "$_src/core/SkBlitter_ARGB32.cpp", + "$_src/core/SkBlitter_RGB565.cpp", "$_src/core/SkBlitter_Sprite.cpp", "$_src/core/SkBlurImageFilter.cpp", "$_src/core/SkBuffer.cpp", @@ -292,6 +293,7 @@ "$_src/core/SkSpecialSurface.h", "$_src/core/SkSpinlock.cpp", "$_src/core/SkSpriteBlitter_ARGB32.cpp", + "$_src/core/SkSpriteBlitter_RGB565.cpp", "$_src/core/SkSpriteBlitter.h", "$_src/core/SkSpriteBlitterTemplate.h", "$_src/core/SkStream.cpp",
diff --git a/src/core/SkBlitter.cpp b/src/core/SkBlitter.cpp index 45a5698..daee460 100644 --- a/src/core/SkBlitter.cpp +++ b/src/core/SkBlitter.cpp
@@ -818,6 +818,11 @@ return true; } + // Added support only for shaders (and other constraints) for android + if (device.colorType() == kRGB_565_SkColorType) { + return false; + } + return device.colorType() != kN32_SkColorType; #endif } @@ -954,6 +959,13 @@ blitter = alloc->make<SkARGB32_Blitter>(device, *paint); } break; + case kRGB_565_SkColorType: + if (shader && SkRGB565_Shader_Blitter::Supports(device, *paint)) { + blitter = alloc->make<SkRGB565_Shader_Blitter>(device, *paint, shaderContext); + } else { + blitter = SkCreateRasterPipelineBlitter(device, *paint, matrix, alloc); + } + break; default: // should have been handled via raster pipeline.
diff --git a/src/core/SkBlitter_RGB565.cpp b/src/core/SkBlitter_RGB565.cpp new file mode 100644 index 0000000..72977c1 --- /dev/null +++ b/src/core/SkBlitter_RGB565.cpp
@@ -0,0 +1,142 @@ +/* + * 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 "SkCoreBlitters.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkXfermodePriv.h" +#include "SkBlitMask.h" +#include "SkColorPriv.h" + +#include "SkNx.h" + +static void D16_S32X_src(uint16_t dst[], const SkPMColor src[], int count, uint8_t coverage) { + SkASSERT(coverage == 0xFF); + for (int i = 0; i < count; ++i) { + dst[i] = SkPixel32ToPixel16(src[i]); + } +} + +static void D16_S32X_src_coverage(uint16_t dst[], const SkPMColor src[], int count, + uint8_t coverage) { + switch (coverage) { + case 0: break; + case 0xFF: + for (int i = 0; i < count; ++i) { + dst[i] = SkPixel32ToPixel16(src[i]); + } + break; + default: + unsigned scale = coverage + (coverage >> 7); + for (int i = 0; i < count; ++i) { + dst[i] = SkSrcOver32To16(SkAlphaMulQ(src[i], scale), dst[i]); + } + break; + } +} + +static void D16_S32A_srcover(uint16_t dst[], const SkPMColor src[], int count, uint8_t coverage) { + SkASSERT(coverage == 0xFF); + for (int i = 0; i < count; ++i) { + dst[i] = SkSrcOver32To16(src[i], dst[i]); + } +} + +static void D16_S32A_srcover_coverage(uint16_t dst[], const SkPMColor src[], int count, + uint8_t coverage) { + switch (coverage) { + case 0: break; + case 0xFF: + for (int i = 0; i < count; ++i) { + dst[i] = SkSrcOver32To16(src[i], dst[i]); + } + break; + default: + unsigned scale = coverage + (coverage >> 7); + for (int i = 0; i < count; ++i) { + dst[i] = SkSrcOver32To16(SkAlphaMulQ(src[i], scale), dst[i]); + } + break; + } +} + +bool SkRGB565_Shader_Blitter::Supports(const SkPixmap& device, const SkPaint& paint) { + if (device.colorType() != kRGB_565_SkColorType) { + return false; + } + if (device.colorSpace()) { + return false; + } + if (paint.getBlendMode() != SkBlendMode::kSrcOver && + paint.getBlendMode() != SkBlendMode::kSrc) { + return false; + } + if (paint.isLCDRenderText()) { + return false; + } + if (paint.isDither()) { + return false; + } + return true; +} + +SkRGB565_Shader_Blitter::SkRGB565_Shader_Blitter(const SkPixmap& device, + const SkPaint& paint, SkShaderBase::Context* shaderContext) + : INHERITED(device, paint, shaderContext) +{ + SkASSERT(shaderContext); + SkASSERT(Supports(device, paint)); + + fBuffer = (SkPMColor*)sk_malloc_throw(device.width() * (sizeof(SkPMColor))); + + bool isOpaque = SkToBool(shaderContext->getFlags() & SkShaderBase::kOpaqueAlpha_Flag); + + if (paint.getBlendMode() == SkBlendMode::kSrc || isOpaque) { + fBlend = D16_S32X_src; + fBlendCoverage = D16_S32X_src_coverage; + } else { // srcover + fBlend = isOpaque ? D16_S32X_src : D16_S32A_srcover; + fBlendCoverage = isOpaque ? D16_S32X_src_coverage : D16_S32A_srcover_coverage; + } +} + +SkRGB565_Shader_Blitter::~SkRGB565_Shader_Blitter() { + sk_free(fBuffer); +} + +void SkRGB565_Shader_Blitter::blitH(int x, int y, int width) { + SkASSERT(x >= 0 && y >= 0 && x + width <= fDevice.width()); + + uint16_t* device = fDevice.writable_addr16(x, y); + + SkPMColor* span = fBuffer; + fShaderContext->shadeSpan(x, y, span, width); + fBlend(device, span, width, 0xFF); +} + +void SkRGB565_Shader_Blitter::blitAntiH(int x, int y, const SkAlpha coverage[], + const int16_t runs[]) { + SkPMColor* span = fBuffer; + uint16_t* device = fDevice.writable_addr16(x, y); + auto* shaderContext = fShaderContext; + + for (;;) { + int count = *runs; + if (count <= 0) { + break; + } + int aa = *coverage; + if (aa) { + shaderContext->shadeSpan(x, y, span, count); + fBlendCoverage(device, span, count, aa); + } + device += count; + runs += count; + coverage += count; + x += count; + } +}
diff --git a/src/core/SkBlitter_Sprite.cpp b/src/core/SkBlitter_Sprite.cpp index 9f34391..cb8d810 100644 --- a/src/core/SkBlitter_Sprite.cpp +++ b/src/core/SkBlitter_Sprite.cpp
@@ -186,8 +186,20 @@ if (!blitter && SkSpriteBlitter_Memcpy::Supports(dst, source, paint)) { blitter = allocator->make<SkSpriteBlitter_Memcpy>(source); } - if (!blitter && !dst.colorSpace() && dst.colorType() == kN32_SkColorType) { - blitter = SkSpriteBlitter::ChooseL32(source, paint, allocator); + if (!blitter && !dst.colorSpace()) { + switch (dst.colorType()) { + case kN32_SkColorType: + blitter = SkSpriteBlitter::ChooseL32(source, paint, allocator); + break; + case kRGB_565_SkColorType: + blitter = SkSpriteBlitter::ChooseL565(source, paint, allocator); + break; + case kAlpha_8_SkColorType: + blitter = SkSpriteBlitter::ChooseLA8(source, paint, allocator); + break; + default: + break; + } } if (!blitter) { blitter = allocator->make<SkRasterPipelineSpriteBlitter>(source, allocator);
diff --git a/src/core/SkCoreBlitters.h b/src/core/SkCoreBlitters.h index 47b2017..9d227e9 100644 --- a/src/core/SkCoreBlitters.h +++ b/src/core/SkCoreBlitters.h
@@ -137,6 +137,27 @@ typedef SkShaderBlitter INHERITED; }; +/////////////////////////////////////////////////////////////////////////////////////////////////// + +typedef void (*SkS32D16BlendProc)(uint16_t*, const SkPMColor*, int, uint8_t); + +class SkRGB565_Shader_Blitter : public SkShaderBlitter { +public: + SkRGB565_Shader_Blitter(const SkPixmap& device, const SkPaint&, SkShaderBase::Context*); + ~SkRGB565_Shader_Blitter() override; + void blitH(int x, int y, int width) override; + void blitAntiH(int x, int y, const SkAlpha[], const int16_t[]) override; + + static bool Supports(const SkPixmap& device, const SkPaint&); + +private: + SkPMColor* fBuffer; + SkS32D16BlendProc fBlend; + SkS32D16BlendProc fBlendCoverage; + + typedef SkShaderBlitter INHERITED; +}; + /////////////////////////////////////////////////////////////////////////////// // Neither of these ever returns nullptr, but this first factory may return a SkNullBlitter.
diff --git a/src/core/SkSpriteBlitter.h b/src/core/SkSpriteBlitter.h index 9f10dbf..4231367 100644 --- a/src/core/SkSpriteBlitter.h +++ b/src/core/SkSpriteBlitter.h
@@ -33,6 +33,8 @@ void blitRect(int x, int y, int width, int height) override = 0; static SkSpriteBlitter* ChooseL32(const SkPixmap& source, const SkPaint&, SkArenaAlloc*); + static SkSpriteBlitter* ChooseL565(const SkPixmap& source, const SkPaint&, SkArenaAlloc*); + static SkSpriteBlitter* ChooseLA8(const SkPixmap& source, const SkPaint&, SkArenaAlloc*); protected: SkPixmap fDst;
diff --git a/src/core/SkSpriteBlitter_RGB565.cpp b/src/core/SkSpriteBlitter_RGB565.cpp new file mode 100644 index 0000000..8b78099 --- /dev/null +++ b/src/core/SkSpriteBlitter_RGB565.cpp
@@ -0,0 +1,179 @@ +/* + * 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 "SkSpriteBlitter.h" +#include "SkArenaAlloc.h" +#include "SkBlitRow.h" +#include "SkColorFilter.h" +#include "SkColorPriv.h" +#include "SkTemplates.h" +#include "SkUtils.h" +#include "SkXfermodePriv.h" + +/////////////////////////////////////////////////////////////////////////////// + +static void S32_src(uint16_t dst[], const SkPMColor src[], int count) { + for (int i = 0; i < count; ++i) { + dst[i] = SkPixel32ToPixel16(src[i]); + } +} + +static void S32_srcover(uint16_t dst[], const SkPMColor src[], int count) { + for (int i = 0; i < count; ++i) { + dst[i] = SkSrcOver32To16(src[i], dst[i]); + } +} + +class Sprite_D16_S32 : public SkSpriteBlitter { +public: + Sprite_D16_S32(const SkPixmap& src, SkBlendMode mode) : INHERITED(src) { + SkASSERT(src.colorType() == kN32_SkColorType); + SkASSERT(mode == SkBlendMode::kSrc || mode == SkBlendMode::kSrcOver); + + fUseSrcOver = (mode == SkBlendMode::kSrcOver) && !src.isOpaque(); + } + + void blitRect(int x, int y, int width, int height) override { + SkASSERT(width > 0 && height > 0); + uint16_t* SK_RESTRICT dst = fDst.writable_addr16(x, y); + const uint32_t* SK_RESTRICT src = fSource.addr32(x - fLeft, y - fTop); + size_t dstRB = fDst.rowBytes(); + size_t srcRB = fSource.rowBytes(); + + do { + if (fUseSrcOver) { + S32_srcover(dst, src, width); + } else { + S32_src(dst, src, width); + } + + dst = (uint16_t* SK_RESTRICT)((char*)dst + dstRB); + src = (const uint32_t* SK_RESTRICT)((const char*)src + srcRB); + } while (--height != 0); + } + +private: + bool fUseSrcOver; + + typedef SkSpriteBlitter INHERITED; +}; + +SkSpriteBlitter* SkSpriteBlitter::ChooseL565(const SkPixmap& source, const SkPaint& paint, + SkArenaAlloc* allocator) { + SkASSERT(allocator != nullptr); + + if (paint.getColorFilter() != nullptr) { + return nullptr; + } + if (paint.getMaskFilter() != nullptr) { + return nullptr; + } + + U8CPU alpha = paint.getAlpha(); + if (alpha != 0xFF) { + return nullptr; + } + + if (source.colorType() == kN32_SkColorType) { + switch (paint.getBlendMode()) { + case SkBlendMode::kSrc: + case SkBlendMode::kSrcOver: + return allocator->make<Sprite_D16_S32>(source, paint.getBlendMode()); + default: + break; + } + } + return nullptr; +} + +////////////////////////////////////////////////////////////////////////////////////////////////// + +static unsigned div255(unsigned a, unsigned b) { + return (a * b * 257 + 127) >> 16; +} + +static void S32_src_da8(uint8_t dst[], const SkPMColor src[], int count) { + for (int i = 0; i < count; ++i) { + dst[i] = SkGetPackedA32(src[i]); + } +} + +static void S32_srcover_da8(uint8_t dst[], const SkPMColor src[], int count) { + for (int i = 0; i < count; ++i) { + SkPMColor c = src[i]; + if (c) { + unsigned a = SkGetPackedA32(c); + if (a == 0xFF) { + dst[i] = 0xFF; + } else { + dst[i] = a + div255(255 - a, dst[i]); + } + } + } +} + +class Sprite_D8_S32 : public SkSpriteBlitter { +public: + Sprite_D8_S32(const SkPixmap& src, SkBlendMode mode) : INHERITED(src) { + SkASSERT(src.colorType() == kN32_SkColorType); + SkASSERT(mode == SkBlendMode::kSrc || mode == SkBlendMode::kSrcOver); + + fUseSrcOver = (mode == SkBlendMode::kSrcOver) && !src.isOpaque(); + } + + void blitRect(int x, int y, int width, int height) override { + SkASSERT(width > 0 && height > 0); + uint8_t* SK_RESTRICT dst = fDst.writable_addr8(x, y); + const uint32_t* SK_RESTRICT src = fSource.addr32(x - fLeft, y - fTop); + size_t dstRB = fDst.rowBytes(); + size_t srcRB = fSource.rowBytes(); + + do { + if (fUseSrcOver) { + S32_srcover_da8(dst, src, width); + } else { + S32_src_da8(dst, src, width); + } + + dst = (uint8_t* SK_RESTRICT)((char*)dst + dstRB); + src = (const uint32_t* SK_RESTRICT)((const char*)src + srcRB); + } while (--height != 0); + } + +private: + bool fUseSrcOver; + + typedef SkSpriteBlitter INHERITED; +}; + +SkSpriteBlitter* SkSpriteBlitter::ChooseLA8(const SkPixmap& source, const SkPaint& paint, + SkArenaAlloc* allocator) { + SkASSERT(allocator != nullptr); + + if (paint.getColorFilter() != nullptr) { + return nullptr; + } + if (paint.getMaskFilter() != nullptr) { + return nullptr; + } + + U8CPU alpha = paint.getAlpha(); + if (alpha != 0xFF) { + return nullptr; + } + + if (source.colorType() == kN32_SkColorType) { + switch (paint.getBlendMode()) { + case SkBlendMode::kSrc: + case SkBlendMode::kSrcOver: + return allocator->make<Sprite_D8_S32>(source, paint.getBlendMode()); + default: + break; + } + } + return nullptr; +}