diff options
Diffstat (limited to 'gfx/skia/skia/src/core/SkBlitter_Sprite.cpp')
-rw-r--r-- | gfx/skia/skia/src/core/SkBlitter_Sprite.cpp | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/gfx/skia/skia/src/core/SkBlitter_Sprite.cpp b/gfx/skia/skia/src/core/SkBlitter_Sprite.cpp new file mode 100644 index 0000000000..ac38d1bbc9 --- /dev/null +++ b/gfx/skia/skia/src/core/SkBlitter_Sprite.cpp @@ -0,0 +1,228 @@ +/* + * 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 "include/core/SkColorSpace.h" +#include "src/base/SkArenaAlloc.h" +#include "src/core/SkColorSpacePriv.h" +#include "src/core/SkColorSpaceXformSteps.h" +#include "src/core/SkCoreBlitters.h" +#include "src/core/SkImageInfoPriv.h" +#include "src/core/SkOpts.h" +#include "src/core/SkRasterPipeline.h" +#include "src/core/SkSpriteBlitter.h" +#include "src/core/SkVMBlitter.h" + +extern bool gUseSkVMBlitter; +extern bool gSkForceRasterPipelineBlitter; + +SkSpriteBlitter::SkSpriteBlitter(const SkPixmap& source) + : fSource(source) {} + +bool SkSpriteBlitter::setup(const SkPixmap& dst, int left, int top, const SkPaint& paint) { + fDst = dst; + fLeft = left; + fTop = top; + fPaint = &paint; + return true; +} + +void SkSpriteBlitter::blitH(int x, int y, int width) { + SkDEBUGFAIL("how did we get here?"); + + // Fallback to blitRect. + this->blitRect(x, y, width, 1); +} + +void SkSpriteBlitter::blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) { + SkDEBUGFAIL("how did we get here?"); + + // No fallback strategy. +} + +void SkSpriteBlitter::blitV(int x, int y, int height, SkAlpha alpha) { + SkDEBUGFAIL("how did we get here?"); + + // Fall back to superclass if the code gets here in release mode. + INHERITED::blitV(x, y, height, alpha); +} + +void SkSpriteBlitter::blitMask(const SkMask& mask, const SkIRect& clip) { + SkDEBUGFAIL("how did we get here?"); + + // Fall back to superclass if the code gets here in release mode. + INHERITED::blitMask(mask, clip); +} + +/////////////////////////////////////////////////////////////////////////////// + +class SkSpriteBlitter_Memcpy final : public SkSpriteBlitter { +public: + static bool Supports(const SkPixmap& dst, const SkPixmap& src, const SkPaint& paint) { + // the caller has already inspected the colorspace on src and dst + SkASSERT(0 == SkColorSpaceXformSteps(src,dst).flags.mask()); + + if (dst.colorType() != src.colorType()) { + return false; + } + if (paint.getMaskFilter() || paint.getColorFilter() || paint.getImageFilter()) { + return false; + } + if (0xFF != paint.getAlpha()) { + return false; + } + const auto mode = paint.asBlendMode(); + return mode == SkBlendMode::kSrc || (mode == SkBlendMode::kSrcOver && src.isOpaque()); + } + + SkSpriteBlitter_Memcpy(const SkPixmap& src) + : INHERITED(src) {} + + void blitRect(int x, int y, int width, int height) override { + SkASSERT(fDst.colorType() == fSource.colorType()); + SkASSERT(width > 0 && height > 0); + + char* dst = (char*)fDst.writable_addr(x, y); + const char* src = (const char*)fSource.addr(x - fLeft, y - fTop); + const size_t dstRB = fDst.rowBytes(); + const size_t srcRB = fSource.rowBytes(); + const size_t bytesToCopy = width << fSource.shiftPerPixel(); + + while (height --> 0) { + memcpy(dst, src, bytesToCopy); + dst += dstRB; + src += srcRB; + } + } + +private: + using INHERITED = SkSpriteBlitter; +}; + +class SkRasterPipelineSpriteBlitter : public SkSpriteBlitter { +public: + SkRasterPipelineSpriteBlitter(const SkPixmap& src, SkArenaAlloc* alloc, + sk_sp<SkShader> clipShader) + : INHERITED(src) + , fAlloc(alloc) + , fBlitter(nullptr) + , fSrcPtr{nullptr, 0} + , fClipShader(std::move(clipShader)) + {} + + bool setup(const SkPixmap& dst, int left, int top, const SkPaint& paint) override { + fDst = dst; + fLeft = left; + fTop = top; + fPaintColor = paint.getColor4f(); + + SkRasterPipeline p(fAlloc); + p.append_load(fSource.colorType(), &fSrcPtr); + + if (SkColorTypeIsAlphaOnly(fSource.colorType())) { + // The color for A8 images comes from the (sRGB) paint color. + p.append_set_rgb(fAlloc, fPaintColor); + p.append(SkRasterPipelineOp::premul); + } + if (auto dstCS = fDst.colorSpace()) { + auto srcCS = fSource.colorSpace(); + if (!srcCS || SkColorTypeIsAlphaOnly(fSource.colorType())) { + // We treat untagged images as sRGB. + // Alpha-only images get their r,g,b from the paint color, so they're also sRGB. + srcCS = sk_srgb_singleton(); + } + auto srcAT = fSource.isOpaque() ? kOpaque_SkAlphaType + : kPremul_SkAlphaType; + fAlloc->make<SkColorSpaceXformSteps>(srcCS, srcAT, + dstCS, kPremul_SkAlphaType) + ->apply(&p); + } + if (fPaintColor.fA != 1.0f) { + p.append(SkRasterPipelineOp::scale_1_float, &fPaintColor.fA); + } + + bool is_opaque = fSource.isOpaque() && fPaintColor.fA == 1.0f; + fBlitter = SkCreateRasterPipelineBlitter(fDst, paint, p, is_opaque, fAlloc, fClipShader); + return fBlitter != nullptr; + } + + void blitRect(int x, int y, int width, int height) override { + fSrcPtr.stride = fSource.rowBytesAsPixels(); + + // We really want fSrcPtr.pixels = fSource.addr(-fLeft, -fTop) here, but that asserts. + // Instead we ask for addr(-fLeft+x, -fTop+y), then back up (x,y) manually. + // Representing bpp as a size_t keeps all this math in size_t instead of int, + // which could wrap around with large enough fSrcPtr.stride and y. + size_t bpp = fSource.info().bytesPerPixel(); + fSrcPtr.pixels = (char*)fSource.addr(-fLeft+x, -fTop+y) - bpp * x + - bpp * y * fSrcPtr.stride; + + fBlitter->blitRect(x,y,width,height); + } + +private: + SkArenaAlloc* fAlloc; + SkBlitter* fBlitter; + SkRasterPipeline_MemoryCtx fSrcPtr; + SkColor4f fPaintColor; + sk_sp<SkShader> fClipShader; + + using INHERITED = SkSpriteBlitter; +}; + +// returning null means the caller will call SkBlitter::Choose() and +// have wrapped the source bitmap inside a shader +SkBlitter* SkBlitter::ChooseSprite(const SkPixmap& dst, const SkPaint& paint, + const SkPixmap& source, int left, int top, + SkArenaAlloc* alloc, sk_sp<SkShader> clipShader) { + /* We currently ignore antialiasing and filtertype, meaning we will take our + special blitters regardless of these settings. Ignoring filtertype seems fine + since by definition there is no scale in the matrix. Ignoring antialiasing is + a bit of a hack, since we "could" pass in the fractional left/top for the bitmap, + and respect that by blending the edges of the bitmap against the device. To support + this we could either add more special blitters here, or detect antialiasing in the + paint and return null if it is set, forcing the client to take the slow shader case + (which does respect soft edges). + */ + SkASSERT(alloc != nullptr); + + if (gUseSkVMBlitter) { + return SkVMBlitter::Make(dst, paint, source,left,top, alloc, std::move(clipShader)); + } + + // TODO: in principle SkRasterPipelineSpriteBlitter could be made to handle this. + if (source.alphaType() == kUnpremul_SkAlphaType) { + return nullptr; + } + + SkSpriteBlitter* blitter = nullptr; + + if (gSkForceRasterPipelineBlitter) { + // Do not use any of these optimized memory blitters + } else if (0 == SkColorSpaceXformSteps(source,dst).flags.mask() && !clipShader) { + if (!blitter && SkSpriteBlitter_Memcpy::Supports(dst, source, paint)) { + blitter = alloc->make<SkSpriteBlitter_Memcpy>(source); + } + if (!blitter) { + switch (dst.colorType()) { + case kN32_SkColorType: + blitter = SkSpriteBlitter::ChooseL32(source, paint, alloc); + break; + default: + break; + } + } + } + if (!blitter && !paint.getMaskFilter()) { + blitter = alloc->make<SkRasterPipelineSpriteBlitter>(source, alloc, clipShader); + } + + if (blitter && blitter->setup(dst, left,top, paint)) { + return blitter; + } + + return SkVMBlitter::Make(dst, paint, source,left,top, alloc, std::move(clipShader)); +} |