1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2019 Google Inc. 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#include "include/private/SkImageInfoPriv.h" 9cb93a386Sopenharmony_ci#include "include/private/SkMacros.h" 10cb93a386Sopenharmony_ci#include "src/core/SkArenaAlloc.h" 11cb93a386Sopenharmony_ci#include "src/core/SkBlendModePriv.h" 12cb93a386Sopenharmony_ci#include "src/core/SkBlenderBase.h" 13cb93a386Sopenharmony_ci#include "src/core/SkColorFilterBase.h" 14cb93a386Sopenharmony_ci#include "src/core/SkColorSpacePriv.h" 15cb93a386Sopenharmony_ci#include "src/core/SkColorSpaceXformSteps.h" 16cb93a386Sopenharmony_ci#include "src/core/SkCoreBlitters.h" 17cb93a386Sopenharmony_ci#include "src/core/SkLRUCache.h" 18cb93a386Sopenharmony_ci#include "src/core/SkMatrixProvider.h" 19cb93a386Sopenharmony_ci#include "src/core/SkOpts.h" 20cb93a386Sopenharmony_ci#include "src/core/SkPaintPriv.h" 21cb93a386Sopenharmony_ci#include "src/core/SkVM.h" 22cb93a386Sopenharmony_ci#include "src/core/SkVMBlitter.h" 23cb93a386Sopenharmony_ci#include "src/shaders/SkColorFilterShader.h" 24cb93a386Sopenharmony_ci 25cb93a386Sopenharmony_ci#include <cinttypes> 26cb93a386Sopenharmony_ci 27cb93a386Sopenharmony_cinamespace { 28cb93a386Sopenharmony_ci 29cb93a386Sopenharmony_ci // Uniforms set by the Blitter itself, 30cb93a386Sopenharmony_ci // rather than by the Shader, which follow this struct in the skvm::Uniforms buffer. 31cb93a386Sopenharmony_ci struct BlitterUniforms { 32cb93a386Sopenharmony_ci int right; // First device x + blit run length n, used to get device x coordinate. 33cb93a386Sopenharmony_ci int y; // Device y coordinate. 34cb93a386Sopenharmony_ci }; 35cb93a386Sopenharmony_ci static_assert(SkIsAlign4(sizeof(BlitterUniforms)), ""); 36cb93a386Sopenharmony_ci inline static constexpr int kBlitterUniformsCount = sizeof(BlitterUniforms) / 4; 37cb93a386Sopenharmony_ci 38cb93a386Sopenharmony_ci static skvm::Coord device_coord(skvm::Builder* p, skvm::Uniforms* uniforms) { 39cb93a386Sopenharmony_ci skvm::I32 dx = p->uniform32(uniforms->base, offsetof(BlitterUniforms, right)) 40cb93a386Sopenharmony_ci - p->index(), 41cb93a386Sopenharmony_ci dy = p->uniform32(uniforms->base, offsetof(BlitterUniforms, y)); 42cb93a386Sopenharmony_ci return { 43cb93a386Sopenharmony_ci to_F32(dx) + 0.5f, 44cb93a386Sopenharmony_ci to_F32(dy) + 0.5f, 45cb93a386Sopenharmony_ci }; 46cb93a386Sopenharmony_ci } 47cb93a386Sopenharmony_ci 48cb93a386Sopenharmony_ci struct NoopColorFilter : public SkColorFilterBase { 49cb93a386Sopenharmony_ci skvm::Color onProgram(skvm::Builder*, skvm::Color c, 50cb93a386Sopenharmony_ci const SkColorInfo&, skvm::Uniforms*, SkArenaAlloc*) const override { 51cb93a386Sopenharmony_ci return c; 52cb93a386Sopenharmony_ci } 53cb93a386Sopenharmony_ci 54cb93a386Sopenharmony_ci bool onAppendStages(const SkStageRec&, bool) const override { return true; } 55cb93a386Sopenharmony_ci 56cb93a386Sopenharmony_ci // Only created here, should never be flattened / unflattened. 57cb93a386Sopenharmony_ci Factory getFactory() const override { return nullptr; } 58cb93a386Sopenharmony_ci const char* getTypeName() const override { return "NoopColorFilter"; } 59cb93a386Sopenharmony_ci }; 60cb93a386Sopenharmony_ci 61cb93a386Sopenharmony_ci struct SpriteShader : public SkShaderBase { 62cb93a386Sopenharmony_ci explicit SpriteShader(SkPixmap sprite) : fSprite(sprite) {} 63cb93a386Sopenharmony_ci 64cb93a386Sopenharmony_ci SkPixmap fSprite; 65cb93a386Sopenharmony_ci 66cb93a386Sopenharmony_ci // Only created here temporarily... never serialized. 67cb93a386Sopenharmony_ci Factory getFactory() const override { return nullptr; } 68cb93a386Sopenharmony_ci const char* getTypeName() const override { return "SpriteShader"; } 69cb93a386Sopenharmony_ci 70cb93a386Sopenharmony_ci bool isOpaque() const override { return fSprite.isOpaque(); } 71cb93a386Sopenharmony_ci 72cb93a386Sopenharmony_ci skvm::Color onProgram(skvm::Builder* p, 73cb93a386Sopenharmony_ci skvm::Coord /*device*/, skvm::Coord /*local*/, skvm::Color /*paint*/, 74cb93a386Sopenharmony_ci const SkMatrixProvider&, const SkMatrix* /*localM*/, 75cb93a386Sopenharmony_ci const SkColorInfo& dst, 76cb93a386Sopenharmony_ci skvm::Uniforms* uniforms, SkArenaAlloc*) const override { 77cb93a386Sopenharmony_ci const SkColorType ct = fSprite.colorType(); 78cb93a386Sopenharmony_ci 79cb93a386Sopenharmony_ci skvm::PixelFormat fmt = skvm::SkColorType_to_PixelFormat(ct); 80cb93a386Sopenharmony_ci 81cb93a386Sopenharmony_ci skvm::Color c = p->load(fmt, p->varying(SkColorTypeBytesPerPixel(ct))); 82cb93a386Sopenharmony_ci 83cb93a386Sopenharmony_ci return SkColorSpaceXformSteps{fSprite, dst}.program(p, uniforms, c); 84cb93a386Sopenharmony_ci } 85cb93a386Sopenharmony_ci }; 86cb93a386Sopenharmony_ci 87cb93a386Sopenharmony_ci struct DitherShader : public SkShaderBase { 88cb93a386Sopenharmony_ci explicit DitherShader(sk_sp<SkShader> shader) : fShader(std::move(shader)) {} 89cb93a386Sopenharmony_ci 90cb93a386Sopenharmony_ci sk_sp<SkShader> fShader; 91cb93a386Sopenharmony_ci 92cb93a386Sopenharmony_ci // Only created here temporarily... never serialized. 93cb93a386Sopenharmony_ci Factory getFactory() const override { return nullptr; } 94cb93a386Sopenharmony_ci const char* getTypeName() const override { return "DitherShader"; } 95cb93a386Sopenharmony_ci 96cb93a386Sopenharmony_ci bool isOpaque() const override { return fShader->isOpaque(); } 97cb93a386Sopenharmony_ci 98cb93a386Sopenharmony_ci skvm::Color onProgram(skvm::Builder* p, 99cb93a386Sopenharmony_ci skvm::Coord device, skvm::Coord local, skvm::Color paint, 100cb93a386Sopenharmony_ci const SkMatrixProvider& matrices, const SkMatrix* localM, 101cb93a386Sopenharmony_ci const SkColorInfo& dst, 102cb93a386Sopenharmony_ci skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override { 103cb93a386Sopenharmony_ci // Run our wrapped shader. 104cb93a386Sopenharmony_ci skvm::Color c = as_SB(fShader)->program(p, device,local, paint, 105cb93a386Sopenharmony_ci matrices,localM, dst, uniforms,alloc); 106cb93a386Sopenharmony_ci if (!c) { 107cb93a386Sopenharmony_ci return {}; 108cb93a386Sopenharmony_ci } 109cb93a386Sopenharmony_ci 110cb93a386Sopenharmony_ci float rate = 0.0f; 111cb93a386Sopenharmony_ci switch (dst.colorType()) { 112cb93a386Sopenharmony_ci case kARGB_4444_SkColorType: rate = 1/15.0f; break; 113cb93a386Sopenharmony_ci case kRGB_565_SkColorType: rate = 1/63.0f; break; 114cb93a386Sopenharmony_ci case kGray_8_SkColorType: 115cb93a386Sopenharmony_ci case kRGB_888x_SkColorType: 116cb93a386Sopenharmony_ci case kRGBA_8888_SkColorType: 117cb93a386Sopenharmony_ci case kBGRA_8888_SkColorType: 118cb93a386Sopenharmony_ci case kSRGBA_8888_SkColorType: rate = 1/255.0f; break; 119cb93a386Sopenharmony_ci case kRGB_101010x_SkColorType: 120cb93a386Sopenharmony_ci case kRGBA_1010102_SkColorType: 121cb93a386Sopenharmony_ci case kBGR_101010x_SkColorType: 122cb93a386Sopenharmony_ci case kBGRA_1010102_SkColorType: rate = 1/1023.0f; break; 123cb93a386Sopenharmony_ci 124cb93a386Sopenharmony_ci case kUnknown_SkColorType: 125cb93a386Sopenharmony_ci case kAlpha_8_SkColorType: 126cb93a386Sopenharmony_ci case kRGBA_F16_SkColorType: 127cb93a386Sopenharmony_ci case kRGBA_F16Norm_SkColorType: 128cb93a386Sopenharmony_ci case kRGBA_F32_SkColorType: 129cb93a386Sopenharmony_ci case kR8G8_unorm_SkColorType: 130cb93a386Sopenharmony_ci case kA16_float_SkColorType: 131cb93a386Sopenharmony_ci case kA16_unorm_SkColorType: 132cb93a386Sopenharmony_ci case kR16G16_float_SkColorType: 133cb93a386Sopenharmony_ci case kR16G16_unorm_SkColorType: 134cb93a386Sopenharmony_ci case kR16G16B16A16_unorm_SkColorType: return c; 135cb93a386Sopenharmony_ci } 136cb93a386Sopenharmony_ci 137cb93a386Sopenharmony_ci // See SkRasterPipeline dither stage. 138cb93a386Sopenharmony_ci // This is 8x8 ordered dithering. From here we'll only need dx and dx^dy. 139cb93a386Sopenharmony_ci SkASSERT(local.x.id == device.x.id); 140cb93a386Sopenharmony_ci SkASSERT(local.y.id == device.y.id); 141cb93a386Sopenharmony_ci skvm::I32 X = trunc(device.x - 0.5f), 142cb93a386Sopenharmony_ci Y = X ^ trunc(device.y - 0.5f); 143cb93a386Sopenharmony_ci 144cb93a386Sopenharmony_ci // If X's low bits are abc and Y's def, M is fcebda, 145cb93a386Sopenharmony_ci // 6 bits producing all values [0,63] shuffled over an 8x8 grid. 146cb93a386Sopenharmony_ci skvm::I32 M = shl(Y & 1, 5) 147cb93a386Sopenharmony_ci | shl(X & 1, 4) 148cb93a386Sopenharmony_ci | shl(Y & 2, 2) 149cb93a386Sopenharmony_ci | shl(X & 2, 1) 150cb93a386Sopenharmony_ci | shr(Y & 4, 1) 151cb93a386Sopenharmony_ci | shr(X & 4, 2); 152cb93a386Sopenharmony_ci 153cb93a386Sopenharmony_ci // Scale to [0,1) by /64, then to (-0.5,0.5) using 63/128 (~0.492) as 0.5-ε, 154cb93a386Sopenharmony_ci // and finally scale all that by rate. We keep dither strength strictly 155cb93a386Sopenharmony_ci // within ±0.5 to not change exact values like 0 or 1. 156cb93a386Sopenharmony_ci 157cb93a386Sopenharmony_ci // rate could be a uniform, but since it's based on the destination SkColorType, 158cb93a386Sopenharmony_ci // we can bake it in without hurting the cache hit rate. 159cb93a386Sopenharmony_ci float scale = rate * ( 2/128.0f), 160cb93a386Sopenharmony_ci bias = rate * (-63/128.0f); 161cb93a386Sopenharmony_ci skvm::F32 dither = to_F32(M) * scale + bias; 162cb93a386Sopenharmony_ci c.r += dither; 163cb93a386Sopenharmony_ci c.g += dither; 164cb93a386Sopenharmony_ci c.b += dither; 165cb93a386Sopenharmony_ci 166cb93a386Sopenharmony_ci c.r = clamp(c.r, 0.0f, c.a); 167cb93a386Sopenharmony_ci c.g = clamp(c.g, 0.0f, c.a); 168cb93a386Sopenharmony_ci c.b = clamp(c.b, 0.0f, c.a); 169cb93a386Sopenharmony_ci return c; 170cb93a386Sopenharmony_ci } 171cb93a386Sopenharmony_ci }; 172cb93a386Sopenharmony_ci 173cb93a386Sopenharmony_ci // This is similar to using SkShaders::Color(paint.getColor4f(), nullptr), 174cb93a386Sopenharmony_ci // but uses the blitter-provided paint color uniforms instead of pushing its own. 175cb93a386Sopenharmony_ci struct PaintColorShader : public SkShaderBase { 176cb93a386Sopenharmony_ci explicit PaintColorShader(bool isOpaque) : fIsOpaque(isOpaque) {} 177cb93a386Sopenharmony_ci 178cb93a386Sopenharmony_ci const bool fIsOpaque; 179cb93a386Sopenharmony_ci 180cb93a386Sopenharmony_ci // Only created here temporarily... never serialized. 181cb93a386Sopenharmony_ci Factory getFactory() const override { return nullptr; } 182cb93a386Sopenharmony_ci const char* getTypeName() const override { return "PaintColorShader"; } 183cb93a386Sopenharmony_ci 184cb93a386Sopenharmony_ci bool isOpaque() const override { return fIsOpaque; } 185cb93a386Sopenharmony_ci 186cb93a386Sopenharmony_ci skvm::Color onProgram(skvm::Builder*, 187cb93a386Sopenharmony_ci skvm::Coord, skvm::Coord, skvm::Color paint, 188cb93a386Sopenharmony_ci const SkMatrixProvider&, const SkMatrix*, const SkColorInfo&, 189cb93a386Sopenharmony_ci skvm::Uniforms*, SkArenaAlloc*) const override { 190cb93a386Sopenharmony_ci // Incoming `paint` is unpremul in the destination color space, 191cb93a386Sopenharmony_ci // so we just need to premul it. 192cb93a386Sopenharmony_ci return premul(paint); 193cb93a386Sopenharmony_ci } 194cb93a386Sopenharmony_ci }; 195cb93a386Sopenharmony_ci} // namespace 196cb93a386Sopenharmony_ci 197cb93a386Sopenharmony_cibool SkVMBlitter::Key::operator==(const Key& that) const { 198cb93a386Sopenharmony_ci return this->shader == that.shader 199cb93a386Sopenharmony_ci && this->clip == that.clip 200cb93a386Sopenharmony_ci && this->blender == that.blender 201cb93a386Sopenharmony_ci && this->colorSpace == that.colorSpace 202cb93a386Sopenharmony_ci && this->colorType == that.colorType 203cb93a386Sopenharmony_ci && this->alphaType == that.alphaType 204cb93a386Sopenharmony_ci && this->coverage == that.coverage; 205cb93a386Sopenharmony_ci} 206cb93a386Sopenharmony_ci 207cb93a386Sopenharmony_ciSkVMBlitter::Key SkVMBlitter::Key::withCoverage(Coverage c) const { 208cb93a386Sopenharmony_ci Key k = *this; 209cb93a386Sopenharmony_ci k.coverage = SkToU8(c); 210cb93a386Sopenharmony_ci return k; 211cb93a386Sopenharmony_ci} 212cb93a386Sopenharmony_ci 213cb93a386Sopenharmony_ciSkVMBlitter::Params SkVMBlitter::Params::withCoverage(Coverage c) const { 214cb93a386Sopenharmony_ci Params p = *this; 215cb93a386Sopenharmony_ci p.coverage = c; 216cb93a386Sopenharmony_ci return p; 217cb93a386Sopenharmony_ci} 218cb93a386Sopenharmony_ci 219cb93a386Sopenharmony_ciSkVMBlitter::Params SkVMBlitter::EffectiveParams(const SkPixmap& device, 220cb93a386Sopenharmony_ci const SkPixmap* sprite, 221cb93a386Sopenharmony_ci SkPaint paint, 222cb93a386Sopenharmony_ci const SkMatrixProvider& matrices, 223cb93a386Sopenharmony_ci sk_sp<SkShader> clip) { 224cb93a386Sopenharmony_ci // Sprites take priority over any shader. (There's rarely one set, and it's meaningless.) 225cb93a386Sopenharmony_ci if (sprite) { 226cb93a386Sopenharmony_ci paint.setShader(sk_make_sp<SpriteShader>(*sprite)); 227cb93a386Sopenharmony_ci } 228cb93a386Sopenharmony_ci 229cb93a386Sopenharmony_ci // Normal blitters will have already folded color filters into their shader, 230cb93a386Sopenharmony_ci // but we may still need to do that here for SpriteShaders. 231cb93a386Sopenharmony_ci if (paint.getColorFilter()) { 232cb93a386Sopenharmony_ci SkPaintPriv::RemoveColorFilter(&paint, device.colorSpace()); 233cb93a386Sopenharmony_ci } 234cb93a386Sopenharmony_ci SkASSERT(!paint.getColorFilter()); 235cb93a386Sopenharmony_ci 236cb93a386Sopenharmony_ci // If there's no explicit shader, the paint color is the shader, 237cb93a386Sopenharmony_ci // but if there is a shader, it's modulated by the paint alpha. 238cb93a386Sopenharmony_ci sk_sp<SkShader> shader = paint.refShader(); 239cb93a386Sopenharmony_ci if (!shader) { 240cb93a386Sopenharmony_ci shader = sk_make_sp<PaintColorShader>(paint.getColor4f().isOpaque()); 241cb93a386Sopenharmony_ci } else if (paint.getAlphaf() < 1.0f) { 242cb93a386Sopenharmony_ci shader = sk_make_sp<SkColorFilterShader>(std::move(shader), 243cb93a386Sopenharmony_ci paint.getAlphaf(), 244cb93a386Sopenharmony_ci sk_make_sp<NoopColorFilter>()); 245cb93a386Sopenharmony_ci } 246cb93a386Sopenharmony_ci 247cb93a386Sopenharmony_ci // Add dither to the end of the shader pipeline if requested and needed. 248cb93a386Sopenharmony_ci if (paint.isDither() && !as_SB(shader)->isConstant()) { 249cb93a386Sopenharmony_ci shader = sk_make_sp<DitherShader>(std::move(shader)); 250cb93a386Sopenharmony_ci } 251cb93a386Sopenharmony_ci 252cb93a386Sopenharmony_ci // Add the blender. 253cb93a386Sopenharmony_ci sk_sp<SkBlender> blender = paint.refBlender(); 254cb93a386Sopenharmony_ci if (!blender) { 255cb93a386Sopenharmony_ci blender = SkBlender::Mode(SkBlendMode::kSrcOver); 256cb93a386Sopenharmony_ci } 257cb93a386Sopenharmony_ci 258cb93a386Sopenharmony_ci // The most common blend mode is SrcOver, and it can be strength-reduced 259cb93a386Sopenharmony_ci // _greatly_ to Src mode when the shader is opaque. 260cb93a386Sopenharmony_ci // 261cb93a386Sopenharmony_ci // In general all the information we use to make decisions here need to 262cb93a386Sopenharmony_ci // be reflected in Params and Key to make program caching sound, and it 263cb93a386Sopenharmony_ci // might appear that shader->isOpaque() is a property of the shader's 264cb93a386Sopenharmony_ci // uniforms than its fundamental program structure and so unsafe to use. 265cb93a386Sopenharmony_ci // 266cb93a386Sopenharmony_ci // Opacity is such a powerful property that SkShaderBase::program() 267cb93a386Sopenharmony_ci // forces opacity for any shader subclass that claims isOpaque(), so 268cb93a386Sopenharmony_ci // the opaque bit is strongly guaranteed to be part of the program and 269cb93a386Sopenharmony_ci // not just a property of the uniforms. The shader program hash includes 270cb93a386Sopenharmony_ci // this information, making it safe to use anywhere in the blitter codegen. 271cb93a386Sopenharmony_ci if (as_BB(blender)->asBlendMode() == SkBlendMode::kSrcOver && shader->isOpaque()) { 272cb93a386Sopenharmony_ci blender = SkBlender::Mode(SkBlendMode::kSrc); 273cb93a386Sopenharmony_ci } 274cb93a386Sopenharmony_ci 275cb93a386Sopenharmony_ci SkColor4f paintColor = paint.getColor4f(); 276cb93a386Sopenharmony_ci SkColorSpaceXformSteps{sk_srgb_singleton(), kUnpremul_SkAlphaType, 277cb93a386Sopenharmony_ci device.colorSpace(), kUnpremul_SkAlphaType} 278cb93a386Sopenharmony_ci .apply(paintColor.vec()); 279cb93a386Sopenharmony_ci 280cb93a386Sopenharmony_ci return { 281cb93a386Sopenharmony_ci std::move(shader), 282cb93a386Sopenharmony_ci std::move(clip), 283cb93a386Sopenharmony_ci std::move(blender), 284cb93a386Sopenharmony_ci { device.colorType(), device.alphaType(), device.refColorSpace() }, 285cb93a386Sopenharmony_ci Coverage::Full, // Placeholder... withCoverage() will change as needed. 286cb93a386Sopenharmony_ci paintColor, 287cb93a386Sopenharmony_ci matrices, 288cb93a386Sopenharmony_ci }; 289cb93a386Sopenharmony_ci} 290cb93a386Sopenharmony_ci 291cb93a386Sopenharmony_ciskvm::Color SkVMBlitter::DstColor(skvm::Builder* p, const Params& params) { 292cb93a386Sopenharmony_ci skvm::PixelFormat dstFormat = skvm::SkColorType_to_PixelFormat(params.dst.colorType()); 293cb93a386Sopenharmony_ci skvm::Ptr dst_ptr = p->varying(SkColorTypeBytesPerPixel(params.dst.colorType())); 294cb93a386Sopenharmony_ci return p->load(dstFormat, dst_ptr); 295cb93a386Sopenharmony_ci} 296cb93a386Sopenharmony_ci 297cb93a386Sopenharmony_civoid SkVMBlitter::BuildProgram(skvm::Builder* p, const Params& params, 298cb93a386Sopenharmony_ci skvm::Uniforms* uniforms, SkArenaAlloc* alloc) { 299cb93a386Sopenharmony_ci // First two arguments are always uniforms and the destination buffer. 300cb93a386Sopenharmony_ci uniforms->base = p->uniform(); 301cb93a386Sopenharmony_ci skvm::Ptr dst_ptr = p->varying(SkColorTypeBytesPerPixel(params.dst.colorType())); 302cb93a386Sopenharmony_ci // A SpriteShader (in this file) may next use one argument as its varying source. 303cb93a386Sopenharmony_ci // Subsequent arguments depend on params.coverage: 304cb93a386Sopenharmony_ci // - Full: (no more arguments) 305cb93a386Sopenharmony_ci // - Mask3D: mul varying, add varying, 8-bit coverage varying 306cb93a386Sopenharmony_ci // - MaskA8: 8-bit coverage varying 307cb93a386Sopenharmony_ci // - MaskLCD16: 565 coverage varying 308cb93a386Sopenharmony_ci // - UniformF: float coverage uniform 309cb93a386Sopenharmony_ci 310cb93a386Sopenharmony_ci skvm::Coord device = device_coord(p, uniforms); 311cb93a386Sopenharmony_ci skvm::Color paint = p->uniformColor(params.paint, uniforms); 312cb93a386Sopenharmony_ci 313cb93a386Sopenharmony_ci // See note about arguments above: a SpriteShader will call p->arg() once during program(). 314cb93a386Sopenharmony_ci skvm::Color src = as_SB(params.shader)->program(p, device, /*local=*/device, paint, 315cb93a386Sopenharmony_ci params.matrices, /*localM=*/nullptr, 316cb93a386Sopenharmony_ci params.dst, uniforms, alloc); 317cb93a386Sopenharmony_ci SkASSERT(src); 318cb93a386Sopenharmony_ci if (params.coverage == Coverage::Mask3D) { 319cb93a386Sopenharmony_ci skvm::F32 M = from_unorm(8, p->load8(p->varying<uint8_t>())), 320cb93a386Sopenharmony_ci A = from_unorm(8, p->load8(p->varying<uint8_t>())); 321cb93a386Sopenharmony_ci 322cb93a386Sopenharmony_ci src.r = min(src.r * M + A, src.a); 323cb93a386Sopenharmony_ci src.g = min(src.g * M + A, src.a); 324cb93a386Sopenharmony_ci src.b = min(src.b * M + A, src.a); 325cb93a386Sopenharmony_ci } 326cb93a386Sopenharmony_ci 327cb93a386Sopenharmony_ci // GL clamps all its color channels to limits of the format just before the blend step (~here). 328cb93a386Sopenharmony_ci // TODO: Below, we also clamp after the blend step. If we can prove that none of the work here 329cb93a386Sopenharmony_ci // (especially blending, for built-in blend modes) will produce colors outside [0, 1] we may be 330cb93a386Sopenharmony_ci // able to skip the second clamp. For now, we clamp twice. 331cb93a386Sopenharmony_ci if (SkColorTypeIsNormalized(params.dst.colorType())) { 332cb93a386Sopenharmony_ci src = clamp01(src); 333cb93a386Sopenharmony_ci } 334cb93a386Sopenharmony_ci 335cb93a386Sopenharmony_ci // Load the destination color. 336cb93a386Sopenharmony_ci skvm::PixelFormat dstFormat = skvm::SkColorType_to_PixelFormat(params.dst.colorType()); 337cb93a386Sopenharmony_ci skvm::Color dst = p->load(dstFormat, dst_ptr); 338cb93a386Sopenharmony_ci if (params.dst.isOpaque()) { 339cb93a386Sopenharmony_ci // When a destination is known opaque, we may assume it both starts and stays fully 340cb93a386Sopenharmony_ci // opaque, ignoring any math that disagrees. This sometimes trims a little work. 341cb93a386Sopenharmony_ci dst.a = p->splat(1.0f); 342cb93a386Sopenharmony_ci } else if (params.dst.alphaType() == kUnpremul_SkAlphaType) { 343cb93a386Sopenharmony_ci // All our blending works in terms of premul. 344cb93a386Sopenharmony_ci dst = premul(dst); 345cb93a386Sopenharmony_ci } 346cb93a386Sopenharmony_ci 347cb93a386Sopenharmony_ci // Load coverage. 348cb93a386Sopenharmony_ci skvm::Color cov; 349cb93a386Sopenharmony_ci switch (params.coverage) { 350cb93a386Sopenharmony_ci case Coverage::Full: 351cb93a386Sopenharmony_ci cov.r = cov.g = cov.b = cov.a = p->splat(1.0f); 352cb93a386Sopenharmony_ci break; 353cb93a386Sopenharmony_ci 354cb93a386Sopenharmony_ci case Coverage::UniformF: 355cb93a386Sopenharmony_ci cov.r = cov.g = cov.b = cov.a = p->uniformF(p->uniform(), 0); 356cb93a386Sopenharmony_ci break; 357cb93a386Sopenharmony_ci 358cb93a386Sopenharmony_ci case Coverage::Mask3D: 359cb93a386Sopenharmony_ci case Coverage::MaskA8: 360cb93a386Sopenharmony_ci cov.r = cov.g = cov.b = cov.a = from_unorm(8, p->load8(p->varying<uint8_t>())); 361cb93a386Sopenharmony_ci break; 362cb93a386Sopenharmony_ci 363cb93a386Sopenharmony_ci case Coverage::MaskLCD16: { 364cb93a386Sopenharmony_ci skvm::PixelFormat fmt = skvm::SkColorType_to_PixelFormat(kRGB_565_SkColorType); 365cb93a386Sopenharmony_ci cov = p->load(fmt, p->varying<uint16_t>()); 366cb93a386Sopenharmony_ci cov.a = select(src.a < dst.a, min(cov.r, min(cov.g, cov.b)), 367cb93a386Sopenharmony_ci max(cov.r, max(cov.g, cov.b))); 368cb93a386Sopenharmony_ci } break; 369cb93a386Sopenharmony_ci } 370cb93a386Sopenharmony_ci if (params.clip) { 371cb93a386Sopenharmony_ci skvm::Color clip = as_SB(params.clip)->program(p, device, /*local=*/device, paint, 372cb93a386Sopenharmony_ci params.matrices, /*localM=*/nullptr, 373cb93a386Sopenharmony_ci params.dst, uniforms, alloc); 374cb93a386Sopenharmony_ci SkAssertResult(clip); 375cb93a386Sopenharmony_ci cov.r *= clip.a; // We use the alpha channel of clip for all four. 376cb93a386Sopenharmony_ci cov.g *= clip.a; 377cb93a386Sopenharmony_ci cov.b *= clip.a; 378cb93a386Sopenharmony_ci cov.a *= clip.a; 379cb93a386Sopenharmony_ci } 380cb93a386Sopenharmony_ci 381cb93a386Sopenharmony_ci const SkBlenderBase* blender = as_BB(params.blender); 382cb93a386Sopenharmony_ci const auto as_blendmode = blender->asBlendMode(); 383cb93a386Sopenharmony_ci 384cb93a386Sopenharmony_ci // The math for some blend modes lets us fold coverage into src before the blend, which is 385cb93a386Sopenharmony_ci // simpler than the canonical post-blend lerp(). 386cb93a386Sopenharmony_ci bool applyPostBlendCoverage = true; 387cb93a386Sopenharmony_ci if (as_blendmode && 388cb93a386Sopenharmony_ci SkBlendMode_ShouldPreScaleCoverage(as_blendmode.value(), 389cb93a386Sopenharmony_ci params.coverage == Coverage::MaskLCD16)) { 390cb93a386Sopenharmony_ci applyPostBlendCoverage = false; 391cb93a386Sopenharmony_ci src.r *= cov.r; 392cb93a386Sopenharmony_ci src.g *= cov.g; 393cb93a386Sopenharmony_ci src.b *= cov.b; 394cb93a386Sopenharmony_ci src.a *= cov.a; 395cb93a386Sopenharmony_ci } 396cb93a386Sopenharmony_ci 397cb93a386Sopenharmony_ci // Apply our blend function to the computed color. 398cb93a386Sopenharmony_ci src = blender->program(p, src, dst, params.dst, uniforms, alloc); 399cb93a386Sopenharmony_ci 400cb93a386Sopenharmony_ci if (applyPostBlendCoverage) { 401cb93a386Sopenharmony_ci src.r = lerp(dst.r, src.r, cov.r); 402cb93a386Sopenharmony_ci src.g = lerp(dst.g, src.g, cov.g); 403cb93a386Sopenharmony_ci src.b = lerp(dst.b, src.b, cov.b); 404cb93a386Sopenharmony_ci src.a = lerp(dst.a, src.a, cov.a); 405cb93a386Sopenharmony_ci } 406cb93a386Sopenharmony_ci 407cb93a386Sopenharmony_ci if (params.dst.isOpaque()) { 408cb93a386Sopenharmony_ci // (See the note above when loading the destination color.) 409cb93a386Sopenharmony_ci src.a = p->splat(1.0f); 410cb93a386Sopenharmony_ci } else if (params.dst.alphaType() == kUnpremul_SkAlphaType) { 411cb93a386Sopenharmony_ci src = unpremul(src); 412cb93a386Sopenharmony_ci } 413cb93a386Sopenharmony_ci 414cb93a386Sopenharmony_ci // Clamp to fit destination color format if needed. 415cb93a386Sopenharmony_ci if (SkColorTypeIsNormalized(params.dst.colorType())) { 416cb93a386Sopenharmony_ci src = clamp01(src); 417cb93a386Sopenharmony_ci } 418cb93a386Sopenharmony_ci 419cb93a386Sopenharmony_ci // Write it out! 420cb93a386Sopenharmony_ci store(dstFormat, dst_ptr, src); 421cb93a386Sopenharmony_ci} 422cb93a386Sopenharmony_ci 423cb93a386Sopenharmony_ci// If BuildProgram() can't build this program, CacheKey() sets *ok to false. 424cb93a386Sopenharmony_ciSkVMBlitter::Key SkVMBlitter::CacheKey( 425cb93a386Sopenharmony_ci const Params& params, skvm::Uniforms* uniforms, SkArenaAlloc* alloc, bool* ok) { 426cb93a386Sopenharmony_ci // Take care to match buildProgram()'s reuse of the paint color uniforms. 427cb93a386Sopenharmony_ci skvm::Uniform r = uniforms->pushF(params.paint.fR), 428cb93a386Sopenharmony_ci g = uniforms->pushF(params.paint.fG), 429cb93a386Sopenharmony_ci b = uniforms->pushF(params.paint.fB), 430cb93a386Sopenharmony_ci a = uniforms->pushF(params.paint.fA); 431cb93a386Sopenharmony_ci 432cb93a386Sopenharmony_ci auto hash_shader = [&](skvm::Builder& p, const sk_sp<SkShader>& shader, 433cb93a386Sopenharmony_ci skvm::Color* outColor) { 434cb93a386Sopenharmony_ci const SkShaderBase* sb = as_SB(shader); 435cb93a386Sopenharmony_ci 436cb93a386Sopenharmony_ci skvm::Coord device = device_coord(&p, uniforms); 437cb93a386Sopenharmony_ci skvm::Color paint = { 438cb93a386Sopenharmony_ci p.uniformF(r), 439cb93a386Sopenharmony_ci p.uniformF(g), 440cb93a386Sopenharmony_ci p.uniformF(b), 441cb93a386Sopenharmony_ci p.uniformF(a), 442cb93a386Sopenharmony_ci }; 443cb93a386Sopenharmony_ci 444cb93a386Sopenharmony_ci uint64_t hash = 0; 445cb93a386Sopenharmony_ci *outColor = sb->program(&p, device, /*local=*/device, paint, params.matrices, 446cb93a386Sopenharmony_ci /*localM=*/nullptr, params.dst, uniforms, alloc); 447cb93a386Sopenharmony_ci if (*outColor) { 448cb93a386Sopenharmony_ci hash = p.hash(); 449cb93a386Sopenharmony_ci // p.hash() folds in all instructions to produce r,g,b,a but does not know 450cb93a386Sopenharmony_ci // precisely which value we'll treat as which channel. Imagine the shader 451cb93a386Sopenharmony_ci // called std::swap(*r,*b)... it draws differently, but p.hash() is unchanged. 452cb93a386Sopenharmony_ci // We'll fold the hash of their IDs in order to disambiguate. 453cb93a386Sopenharmony_ci const skvm::Val outputs[] = { 454cb93a386Sopenharmony_ci outColor->r.id, 455cb93a386Sopenharmony_ci outColor->g.id, 456cb93a386Sopenharmony_ci outColor->b.id, 457cb93a386Sopenharmony_ci outColor->a.id 458cb93a386Sopenharmony_ci }; 459cb93a386Sopenharmony_ci hash ^= SkOpts::hash(outputs, sizeof(outputs)); 460cb93a386Sopenharmony_ci } else { 461cb93a386Sopenharmony_ci *ok = false; 462cb93a386Sopenharmony_ci } 463cb93a386Sopenharmony_ci return hash; 464cb93a386Sopenharmony_ci }; 465cb93a386Sopenharmony_ci 466cb93a386Sopenharmony_ci // Use this builder for shader, clip and blender, so that color objects that pass 467cb93a386Sopenharmony_ci // from one to the other all 'make sense' -- i.e. have the same builder and/or have 468cb93a386Sopenharmony_ci // meaningful values for the hash. 469cb93a386Sopenharmony_ci // 470cb93a386Sopenharmony_ci // Question: better if we just pass in mock uniform colors, so we don't need to 471cb93a386Sopenharmony_ci // explicitly use the output color from one stage as input to another? 472cb93a386Sopenharmony_ci // 473cb93a386Sopenharmony_ci skvm::Builder p; 474cb93a386Sopenharmony_ci 475cb93a386Sopenharmony_ci // Calculate a hash for the color shader. 476cb93a386Sopenharmony_ci SkASSERT(params.shader); 477cb93a386Sopenharmony_ci skvm::Color src; 478cb93a386Sopenharmony_ci uint64_t shaderHash = hash_shader(p, params.shader, &src); 479cb93a386Sopenharmony_ci 480cb93a386Sopenharmony_ci // Calculate a hash for the clip shader, if one exists. 481cb93a386Sopenharmony_ci uint64_t clipHash = 0; 482cb93a386Sopenharmony_ci if (params.clip) { 483cb93a386Sopenharmony_ci skvm::Color cov; 484cb93a386Sopenharmony_ci clipHash = hash_shader(p, params.clip, &cov); 485cb93a386Sopenharmony_ci if (clipHash == 0) { 486cb93a386Sopenharmony_ci clipHash = 1; 487cb93a386Sopenharmony_ci } 488cb93a386Sopenharmony_ci } 489cb93a386Sopenharmony_ci 490cb93a386Sopenharmony_ci // Calculate a hash for the blender. 491cb93a386Sopenharmony_ci uint64_t blendHash = 0; 492cb93a386Sopenharmony_ci if (auto bm = as_BB(params.blender)->asBlendMode()) { 493cb93a386Sopenharmony_ci blendHash = static_cast<uint8_t>(bm.value()); 494cb93a386Sopenharmony_ci } else if (*ok) { 495cb93a386Sopenharmony_ci const SkBlenderBase* blender = as_BB(params.blender); 496cb93a386Sopenharmony_ci 497cb93a386Sopenharmony_ci skvm::Color dst = DstColor(&p, params); 498cb93a386Sopenharmony_ci skvm::Color outColor = blender->program(&p, src, dst, params.dst, uniforms, alloc); 499cb93a386Sopenharmony_ci if (outColor) { 500cb93a386Sopenharmony_ci blendHash = p.hash(); 501cb93a386Sopenharmony_ci // Like in `hash_shader` above, we must fold the color component IDs into our hash. 502cb93a386Sopenharmony_ci const skvm::Val outputs[] = { 503cb93a386Sopenharmony_ci outColor.r.id, 504cb93a386Sopenharmony_ci outColor.g.id, 505cb93a386Sopenharmony_ci outColor.b.id, 506cb93a386Sopenharmony_ci outColor.a.id 507cb93a386Sopenharmony_ci }; 508cb93a386Sopenharmony_ci blendHash ^= SkOpts::hash(outputs, sizeof(outputs)); 509cb93a386Sopenharmony_ci } else { 510cb93a386Sopenharmony_ci *ok = false; 511cb93a386Sopenharmony_ci } 512cb93a386Sopenharmony_ci if (blendHash == 0) { 513cb93a386Sopenharmony_ci blendHash = 1; 514cb93a386Sopenharmony_ci } 515cb93a386Sopenharmony_ci } 516cb93a386Sopenharmony_ci 517cb93a386Sopenharmony_ci return { 518cb93a386Sopenharmony_ci shaderHash, 519cb93a386Sopenharmony_ci clipHash, 520cb93a386Sopenharmony_ci blendHash, 521cb93a386Sopenharmony_ci params.dst.colorSpace() ? params.dst.colorSpace()->hash() : 0, 522cb93a386Sopenharmony_ci SkToU8(params.dst.colorType()), 523cb93a386Sopenharmony_ci SkToU8(params.dst.alphaType()), 524cb93a386Sopenharmony_ci SkToU8(params.coverage), 525cb93a386Sopenharmony_ci }; 526cb93a386Sopenharmony_ci} 527cb93a386Sopenharmony_ci 528cb93a386Sopenharmony_ciSkVMBlitter::SkVMBlitter(const SkPixmap& device, 529cb93a386Sopenharmony_ci const SkPaint& paint, 530cb93a386Sopenharmony_ci const SkPixmap* sprite, 531cb93a386Sopenharmony_ci SkIPoint spriteOffset, 532cb93a386Sopenharmony_ci const SkMatrixProvider& matrices, 533cb93a386Sopenharmony_ci sk_sp<SkShader> clip, 534cb93a386Sopenharmony_ci bool* ok) 535cb93a386Sopenharmony_ci : fDevice(device) 536cb93a386Sopenharmony_ci , fSprite(sprite ? *sprite : SkPixmap{}) 537cb93a386Sopenharmony_ci , fSpriteOffset(spriteOffset) 538cb93a386Sopenharmony_ci , fUniforms(skvm::UPtr{0}, kBlitterUniformsCount) 539cb93a386Sopenharmony_ci , fParams(EffectiveParams(device, sprite, paint, matrices, std::move(clip))) 540cb93a386Sopenharmony_ci , fKey(CacheKey(fParams, &fUniforms, &fAlloc, ok)) {} 541cb93a386Sopenharmony_ci 542cb93a386Sopenharmony_ciSkVMBlitter::~SkVMBlitter() { 543cb93a386Sopenharmony_ci if (SkLRUCache<Key, skvm::Program>* cache = TryAcquireProgramCache()) { 544cb93a386Sopenharmony_ci auto cache_program = [&](skvm::Program&& program, Coverage coverage) { 545cb93a386Sopenharmony_ci if (!program.empty()) { 546cb93a386Sopenharmony_ci cache->insert_or_update(fKey.withCoverage(coverage), std::move(program)); 547cb93a386Sopenharmony_ci } 548cb93a386Sopenharmony_ci }; 549cb93a386Sopenharmony_ci cache_program(std::move(fBlitH), Coverage::Full); 550cb93a386Sopenharmony_ci cache_program(std::move(fBlitAntiH), Coverage::UniformF); 551cb93a386Sopenharmony_ci cache_program(std::move(fBlitMaskA8), Coverage::MaskA8); 552cb93a386Sopenharmony_ci cache_program(std::move(fBlitMask3D), Coverage::Mask3D); 553cb93a386Sopenharmony_ci cache_program(std::move(fBlitMaskLCD16), Coverage::MaskLCD16); 554cb93a386Sopenharmony_ci 555cb93a386Sopenharmony_ci ReleaseProgramCache(); 556cb93a386Sopenharmony_ci } 557cb93a386Sopenharmony_ci} 558cb93a386Sopenharmony_ci 559cb93a386Sopenharmony_ciSkLRUCache<SkVMBlitter::Key, skvm::Program>* SkVMBlitter::TryAcquireProgramCache() { 560cb93a386Sopenharmony_ci#if defined(SKVM_JIT) 561cb93a386Sopenharmony_ci thread_local static SkLRUCache<Key, skvm::Program> cache{64}; 562cb93a386Sopenharmony_ci return &cache; 563cb93a386Sopenharmony_ci#else 564cb93a386Sopenharmony_ci // iOS now supports thread_local since iOS 9. 565cb93a386Sopenharmony_ci // On the other hand, we'll never be able to JIT there anyway. 566cb93a386Sopenharmony_ci // It's probably fine to not cache any interpreted programs, anywhere. 567cb93a386Sopenharmony_ci return nullptr; 568cb93a386Sopenharmony_ci#endif 569cb93a386Sopenharmony_ci} 570cb93a386Sopenharmony_ci 571cb93a386Sopenharmony_ciSkString SkVMBlitter::DebugName(const Key& key) { 572cb93a386Sopenharmony_ci return SkStringPrintf("Shader-%" PRIx64 "_Clip-%" PRIx64 "_Blender-%" PRIx64 573cb93a386Sopenharmony_ci "_CS-%" PRIx64 "_CT-%d_AT-%d_Cov-%d", 574cb93a386Sopenharmony_ci key.shader, 575cb93a386Sopenharmony_ci key.clip, 576cb93a386Sopenharmony_ci key.blender, 577cb93a386Sopenharmony_ci key.colorSpace, 578cb93a386Sopenharmony_ci key.colorType, 579cb93a386Sopenharmony_ci key.alphaType, 580cb93a386Sopenharmony_ci key.coverage); 581cb93a386Sopenharmony_ci} 582cb93a386Sopenharmony_ci 583cb93a386Sopenharmony_civoid SkVMBlitter::ReleaseProgramCache() {} 584cb93a386Sopenharmony_ci 585cb93a386Sopenharmony_ciskvm::Program SkVMBlitter::buildProgram(Coverage coverage) { 586cb93a386Sopenharmony_ci Key key = fKey.withCoverage(coverage); 587cb93a386Sopenharmony_ci { 588cb93a386Sopenharmony_ci skvm::Program p; 589cb93a386Sopenharmony_ci if (SkLRUCache<Key, skvm::Program>* cache = TryAcquireProgramCache()) { 590cb93a386Sopenharmony_ci if (skvm::Program* found = cache->find(key)) { 591cb93a386Sopenharmony_ci p = std::move(*found); 592cb93a386Sopenharmony_ci } 593cb93a386Sopenharmony_ci ReleaseProgramCache(); 594cb93a386Sopenharmony_ci } 595cb93a386Sopenharmony_ci if (!p.empty()) { 596cb93a386Sopenharmony_ci return p; 597cb93a386Sopenharmony_ci } 598cb93a386Sopenharmony_ci } 599cb93a386Sopenharmony_ci // We don't really _need_ to rebuild fUniforms here. 600cb93a386Sopenharmony_ci // It's just more natural to have effects unconditionally emit them, 601cb93a386Sopenharmony_ci // and more natural to rebuild fUniforms than to emit them into a temporary buffer. 602cb93a386Sopenharmony_ci // fUniforms should reuse the exact same memory, so this is very cheap. 603cb93a386Sopenharmony_ci SkDEBUGCODE(size_t prev = fUniforms.buf.size();) 604cb93a386Sopenharmony_ci fUniforms.buf.resize(kBlitterUniformsCount); 605cb93a386Sopenharmony_ci skvm::Builder builder; 606cb93a386Sopenharmony_ci BuildProgram(&builder, fParams.withCoverage(coverage), &fUniforms, &fAlloc); 607cb93a386Sopenharmony_ci SkASSERTF(fUniforms.buf.size() == prev, 608cb93a386Sopenharmony_ci "%zu, prev was %zu", fUniforms.buf.size(), prev); 609cb93a386Sopenharmony_ci 610cb93a386Sopenharmony_ci skvm::Program program = builder.done(DebugName(key).c_str()); 611cb93a386Sopenharmony_ci if (false) { 612cb93a386Sopenharmony_ci static std::atomic<int> missed{0}, 613cb93a386Sopenharmony_ci total{0}; 614cb93a386Sopenharmony_ci if (!program.hasJIT()) { 615cb93a386Sopenharmony_ci SkDebugf("\ncouldn't JIT %s\n", DebugName(key).c_str()); 616cb93a386Sopenharmony_ci builder.dump(); 617cb93a386Sopenharmony_ci program.dump(); 618cb93a386Sopenharmony_ci 619cb93a386Sopenharmony_ci missed++; 620cb93a386Sopenharmony_ci } 621cb93a386Sopenharmony_ci if (0 == total++) { 622cb93a386Sopenharmony_ci atexit([]{ SkDebugf("SkVMBlitter compiled %d programs, %d without JIT.\n", 623cb93a386Sopenharmony_ci total.load(), missed.load()); }); 624cb93a386Sopenharmony_ci } 625cb93a386Sopenharmony_ci } 626cb93a386Sopenharmony_ci return program; 627cb93a386Sopenharmony_ci} 628cb93a386Sopenharmony_ci 629cb93a386Sopenharmony_civoid SkVMBlitter::updateUniforms(int right, int y) { 630cb93a386Sopenharmony_ci BlitterUniforms uniforms{right, y}; 631cb93a386Sopenharmony_ci memcpy(fUniforms.buf.data(), &uniforms, sizeof(BlitterUniforms)); 632cb93a386Sopenharmony_ci} 633cb93a386Sopenharmony_ci 634cb93a386Sopenharmony_ciconst void* SkVMBlitter::isSprite(int x, int y) const { 635cb93a386Sopenharmony_ci if (fSprite.colorType() != kUnknown_SkColorType) { 636cb93a386Sopenharmony_ci return fSprite.addr(x - fSpriteOffset.x(), 637cb93a386Sopenharmony_ci y - fSpriteOffset.y()); 638cb93a386Sopenharmony_ci } 639cb93a386Sopenharmony_ci return nullptr; 640cb93a386Sopenharmony_ci} 641cb93a386Sopenharmony_ci 642cb93a386Sopenharmony_civoid SkVMBlitter::blitH(int x, int y, int w) { 643cb93a386Sopenharmony_ci if (fBlitH.empty()) { 644cb93a386Sopenharmony_ci fBlitH = this->buildProgram(Coverage::Full); 645cb93a386Sopenharmony_ci } 646cb93a386Sopenharmony_ci this->updateUniforms(x+w, y); 647cb93a386Sopenharmony_ci if (const void* sprite = this->isSprite(x,y)) { 648cb93a386Sopenharmony_ci fBlitH.eval(w, fUniforms.buf.data(), fDevice.addr(x,y), sprite); 649cb93a386Sopenharmony_ci } else { 650cb93a386Sopenharmony_ci fBlitH.eval(w, fUniforms.buf.data(), fDevice.addr(x,y)); 651cb93a386Sopenharmony_ci } 652cb93a386Sopenharmony_ci} 653cb93a386Sopenharmony_ci 654cb93a386Sopenharmony_civoid SkVMBlitter::blitAntiH(int x, int y, const SkAlpha cov[], const int16_t runs[]) { 655cb93a386Sopenharmony_ci if (fBlitAntiH.empty()) { 656cb93a386Sopenharmony_ci fBlitAntiH = this->buildProgram(Coverage::UniformF); 657cb93a386Sopenharmony_ci } 658cb93a386Sopenharmony_ci if (fBlitH.empty()) { 659cb93a386Sopenharmony_ci fBlitH = this->buildProgram(Coverage::Full); 660cb93a386Sopenharmony_ci } 661cb93a386Sopenharmony_ci for (int16_t run = *runs; run > 0; run = *runs) { 662cb93a386Sopenharmony_ci const SkAlpha coverage = *cov; 663cb93a386Sopenharmony_ci if (coverage != 0x00) { 664cb93a386Sopenharmony_ci this->updateUniforms(x+run, y); 665cb93a386Sopenharmony_ci const void* sprite = this->isSprite(x,y); 666cb93a386Sopenharmony_ci if (coverage == 0xFF) { 667cb93a386Sopenharmony_ci if (sprite) { 668cb93a386Sopenharmony_ci fBlitH.eval(run, fUniforms.buf.data(), fDevice.addr(x,y), sprite); 669cb93a386Sopenharmony_ci } else { 670cb93a386Sopenharmony_ci fBlitH.eval(run, fUniforms.buf.data(), fDevice.addr(x,y)); 671cb93a386Sopenharmony_ci } 672cb93a386Sopenharmony_ci } else { 673cb93a386Sopenharmony_ci const float covF = *cov * (1/255.0f); 674cb93a386Sopenharmony_ci if (sprite) { 675cb93a386Sopenharmony_ci fBlitAntiH.eval(run, fUniforms.buf.data(), fDevice.addr(x,y), sprite, &covF); 676cb93a386Sopenharmony_ci } else { 677cb93a386Sopenharmony_ci fBlitAntiH.eval(run, fUniforms.buf.data(), fDevice.addr(x,y), &covF); 678cb93a386Sopenharmony_ci } 679cb93a386Sopenharmony_ci } 680cb93a386Sopenharmony_ci } 681cb93a386Sopenharmony_ci x += run; 682cb93a386Sopenharmony_ci runs += run; 683cb93a386Sopenharmony_ci cov += run; 684cb93a386Sopenharmony_ci } 685cb93a386Sopenharmony_ci} 686cb93a386Sopenharmony_ci 687cb93a386Sopenharmony_civoid SkVMBlitter::blitMask(const SkMask& mask, const SkIRect& clip) { 688cb93a386Sopenharmony_ci if (mask.fFormat == SkMask::kBW_Format) { 689cb93a386Sopenharmony_ci return SkBlitter::blitMask(mask, clip); 690cb93a386Sopenharmony_ci } 691cb93a386Sopenharmony_ci 692cb93a386Sopenharmony_ci const skvm::Program* program = nullptr; 693cb93a386Sopenharmony_ci switch (mask.fFormat) { 694cb93a386Sopenharmony_ci default: SkUNREACHABLE; // ARGB and SDF masks shouldn't make it here. 695cb93a386Sopenharmony_ci 696cb93a386Sopenharmony_ci case SkMask::k3D_Format: 697cb93a386Sopenharmony_ci if (fBlitMask3D.empty()) { 698cb93a386Sopenharmony_ci fBlitMask3D = this->buildProgram(Coverage::Mask3D); 699cb93a386Sopenharmony_ci } 700cb93a386Sopenharmony_ci program = &fBlitMask3D; 701cb93a386Sopenharmony_ci break; 702cb93a386Sopenharmony_ci 703cb93a386Sopenharmony_ci case SkMask::kA8_Format: 704cb93a386Sopenharmony_ci if (fBlitMaskA8.empty()) { 705cb93a386Sopenharmony_ci fBlitMaskA8 = this->buildProgram(Coverage::MaskA8); 706cb93a386Sopenharmony_ci } 707cb93a386Sopenharmony_ci program = &fBlitMaskA8; 708cb93a386Sopenharmony_ci break; 709cb93a386Sopenharmony_ci 710cb93a386Sopenharmony_ci case SkMask::kLCD16_Format: 711cb93a386Sopenharmony_ci if (fBlitMaskLCD16.empty()) { 712cb93a386Sopenharmony_ci fBlitMaskLCD16 = this->buildProgram(Coverage::MaskLCD16); 713cb93a386Sopenharmony_ci } 714cb93a386Sopenharmony_ci program = &fBlitMaskLCD16; 715cb93a386Sopenharmony_ci break; 716cb93a386Sopenharmony_ci } 717cb93a386Sopenharmony_ci 718cb93a386Sopenharmony_ci SkASSERT(program); 719cb93a386Sopenharmony_ci if (program) { 720cb93a386Sopenharmony_ci for (int y = clip.top(); y < clip.bottom(); y++) { 721cb93a386Sopenharmony_ci int x = clip.left(), 722cb93a386Sopenharmony_ci w = clip.width(); 723cb93a386Sopenharmony_ci void* dptr = fDevice.writable_addr(x,y); 724cb93a386Sopenharmony_ci auto mptr = (const uint8_t*)mask.getAddr(x,y); 725cb93a386Sopenharmony_ci this->updateUniforms(x+w,y); 726cb93a386Sopenharmony_ci 727cb93a386Sopenharmony_ci if (program == &fBlitMask3D) { 728cb93a386Sopenharmony_ci size_t plane = mask.computeImageSize(); 729cb93a386Sopenharmony_ci if (const void* sprite = this->isSprite(x,y)) { 730cb93a386Sopenharmony_ci program->eval(w, fUniforms.buf.data(), dptr, sprite, mptr + 1*plane 731cb93a386Sopenharmony_ci , mptr + 2*plane 732cb93a386Sopenharmony_ci , mptr + 0*plane); 733cb93a386Sopenharmony_ci } else { 734cb93a386Sopenharmony_ci program->eval(w, fUniforms.buf.data(), dptr, mptr + 1*plane 735cb93a386Sopenharmony_ci , mptr + 2*plane 736cb93a386Sopenharmony_ci , mptr + 0*plane); 737cb93a386Sopenharmony_ci } 738cb93a386Sopenharmony_ci } else { 739cb93a386Sopenharmony_ci if (const void* sprite = this->isSprite(x,y)) { 740cb93a386Sopenharmony_ci program->eval(w, fUniforms.buf.data(), dptr, sprite, mptr); 741cb93a386Sopenharmony_ci } else { 742cb93a386Sopenharmony_ci program->eval(w, fUniforms.buf.data(), dptr, mptr); 743cb93a386Sopenharmony_ci } 744cb93a386Sopenharmony_ci } 745cb93a386Sopenharmony_ci } 746cb93a386Sopenharmony_ci } 747cb93a386Sopenharmony_ci} 748cb93a386Sopenharmony_ci 749cb93a386Sopenharmony_ciSkVMBlitter* SkVMBlitter::Make(const SkPixmap& device, 750cb93a386Sopenharmony_ci const SkPaint& paint, 751cb93a386Sopenharmony_ci const SkMatrixProvider& matrices, 752cb93a386Sopenharmony_ci SkArenaAlloc* alloc, 753cb93a386Sopenharmony_ci sk_sp<SkShader> clip) { 754cb93a386Sopenharmony_ci bool ok = true; 755cb93a386Sopenharmony_ci SkVMBlitter* blitter = alloc->make<SkVMBlitter>( 756cb93a386Sopenharmony_ci device, paint, /*sprite=*/nullptr, SkIPoint{0,0}, matrices, std::move(clip), &ok); 757cb93a386Sopenharmony_ci return ok ? blitter : nullptr; 758cb93a386Sopenharmony_ci} 759cb93a386Sopenharmony_ci 760cb93a386Sopenharmony_ciSkVMBlitter* SkVMBlitter::Make(const SkPixmap& device, 761cb93a386Sopenharmony_ci const SkPaint& paint, 762cb93a386Sopenharmony_ci const SkPixmap& sprite, 763cb93a386Sopenharmony_ci int left, int top, 764cb93a386Sopenharmony_ci SkArenaAlloc* alloc, 765cb93a386Sopenharmony_ci sk_sp<SkShader> clip) { 766cb93a386Sopenharmony_ci if (paint.getMaskFilter()) { 767cb93a386Sopenharmony_ci // TODO: SkVM support for mask filters? definitely possible! 768cb93a386Sopenharmony_ci return nullptr; 769cb93a386Sopenharmony_ci } 770cb93a386Sopenharmony_ci bool ok = true; 771cb93a386Sopenharmony_ci auto blitter = alloc->make<SkVMBlitter>( 772cb93a386Sopenharmony_ci device, paint, &sprite, SkIPoint{left,top}, 773cb93a386Sopenharmony_ci SkSimpleMatrixProvider{SkMatrix{}}, std::move(clip), &ok); 774cb93a386Sopenharmony_ci return ok ? blitter : nullptr; 775cb93a386Sopenharmony_ci} 776