1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2015 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 "src/shaders/SkImageShader.h" 9cb93a386Sopenharmony_ci 10cb93a386Sopenharmony_ci#include "src/core/SkArenaAlloc.h" 11cb93a386Sopenharmony_ci#include "src/core/SkColorSpacePriv.h" 12cb93a386Sopenharmony_ci#include "src/core/SkColorSpaceXformSteps.h" 13cb93a386Sopenharmony_ci#include "src/core/SkMatrixPriv.h" 14cb93a386Sopenharmony_ci#include "src/core/SkMatrixProvider.h" 15cb93a386Sopenharmony_ci#include "src/core/SkMipmapAccessor.h" 16cb93a386Sopenharmony_ci#include "src/core/SkOpts.h" 17cb93a386Sopenharmony_ci#include "src/core/SkRasterPipeline.h" 18cb93a386Sopenharmony_ci#include "src/core/SkReadBuffer.h" 19cb93a386Sopenharmony_ci#include "src/core/SkSamplingPriv.h" 20cb93a386Sopenharmony_ci#include "src/core/SkScopeExit.h" 21cb93a386Sopenharmony_ci#include "src/core/SkVM.h" 22cb93a386Sopenharmony_ci#include "src/core/SkWriteBuffer.h" 23cb93a386Sopenharmony_ci#include "src/image/SkImage_Base.h" 24cb93a386Sopenharmony_ci#include "src/shaders/SkBitmapProcShader.h" 25cb93a386Sopenharmony_ci#include "src/shaders/SkEmptyShader.h" 26cb93a386Sopenharmony_ci#include "src/shaders/SkTransformShader.h" 27cb93a386Sopenharmony_ci 28cb93a386Sopenharmony_ciSkM44 SkImageShader::CubicResamplerMatrix(float B, float C) { 29cb93a386Sopenharmony_ci#if 0 30cb93a386Sopenharmony_ci constexpr SkM44 kMitchell = SkM44( 1.f/18.f, -9.f/18.f, 15.f/18.f, -7.f/18.f, 31cb93a386Sopenharmony_ci 16.f/18.f, 0.f/18.f, -36.f/18.f, 21.f/18.f, 32cb93a386Sopenharmony_ci 1.f/18.f, 9.f/18.f, 27.f/18.f, -21.f/18.f, 33cb93a386Sopenharmony_ci 0.f/18.f, 0.f/18.f, -6.f/18.f, 7.f/18.f); 34cb93a386Sopenharmony_ci 35cb93a386Sopenharmony_ci constexpr SkM44 kCatmull = SkM44(0.0f, -0.5f, 1.0f, -0.5f, 36cb93a386Sopenharmony_ci 1.0f, 0.0f, -2.5f, 1.5f, 37cb93a386Sopenharmony_ci 0.0f, 0.5f, 2.0f, -1.5f, 38cb93a386Sopenharmony_ci 0.0f, 0.0f, -0.5f, 0.5f); 39cb93a386Sopenharmony_ci 40cb93a386Sopenharmony_ci if (B == 1.0f/3 && C == 1.0f/3) { 41cb93a386Sopenharmony_ci return kMitchell; 42cb93a386Sopenharmony_ci } 43cb93a386Sopenharmony_ci if (B == 0 && C == 0.5f) { 44cb93a386Sopenharmony_ci return kCatmull; 45cb93a386Sopenharmony_ci } 46cb93a386Sopenharmony_ci#endif 47cb93a386Sopenharmony_ci return SkM44( (1.f/6)*B, -(3.f/6)*B - C, (3.f/6)*B + 2*C, - (1.f/6)*B - C, 48cb93a386Sopenharmony_ci 1 - (2.f/6)*B, 0, -3 + (12.f/6)*B + C, 2 - (9.f/6)*B - C, 49cb93a386Sopenharmony_ci (1.f/6)*B, (3.f/6)*B + C, 3 - (15.f/6)*B - 2*C, -2 + (9.f/6)*B + C, 50cb93a386Sopenharmony_ci 0, 0, -C, (1.f/6)*B + C); 51cb93a386Sopenharmony_ci} 52cb93a386Sopenharmony_ci 53cb93a386Sopenharmony_ci/** 54cb93a386Sopenharmony_ci * We are faster in clamp, so always use that tiling when we can. 55cb93a386Sopenharmony_ci */ 56cb93a386Sopenharmony_cistatic SkTileMode optimize(SkTileMode tm, int dimension) { 57cb93a386Sopenharmony_ci SkASSERT(dimension > 0); 58cb93a386Sopenharmony_ci#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK 59cb93a386Sopenharmony_ci // need to update frameworks/base/libs/hwui/tests/unit/SkiaBehaviorTests.cpp:55 to allow 60cb93a386Sopenharmony_ci // for transforming to clamp. 61cb93a386Sopenharmony_ci return tm; 62cb93a386Sopenharmony_ci#else 63cb93a386Sopenharmony_ci // mirror and repeat on a 1px axis are the same as clamping, but decal will still transition to 64cb93a386Sopenharmony_ci // transparent black. 65cb93a386Sopenharmony_ci return (tm != SkTileMode::kDecal && dimension == 1) ? SkTileMode::kClamp : tm; 66cb93a386Sopenharmony_ci#endif 67cb93a386Sopenharmony_ci} 68cb93a386Sopenharmony_ci 69cb93a386Sopenharmony_ciSkImageShader::SkImageShader(sk_sp<SkImage> img, 70cb93a386Sopenharmony_ci SkTileMode tmx, SkTileMode tmy, 71cb93a386Sopenharmony_ci const SkSamplingOptions& sampling, 72cb93a386Sopenharmony_ci const SkMatrix* localMatrix, 73cb93a386Sopenharmony_ci bool clampAsIfUnpremul) 74cb93a386Sopenharmony_ci : INHERITED(localMatrix) 75cb93a386Sopenharmony_ci , fImage(std::move(img)) 76cb93a386Sopenharmony_ci , fSampling(sampling) 77cb93a386Sopenharmony_ci , fTileModeX(optimize(tmx, fImage->width())) 78cb93a386Sopenharmony_ci , fTileModeY(optimize(tmy, fImage->height())) 79cb93a386Sopenharmony_ci , fClampAsIfUnpremul(clampAsIfUnpremul) 80cb93a386Sopenharmony_ci{} 81cb93a386Sopenharmony_ci 82cb93a386Sopenharmony_ci// just used for legacy-unflattening 83cb93a386Sopenharmony_cienum class LegacyFilterEnum { 84cb93a386Sopenharmony_ci kNone, 85cb93a386Sopenharmony_ci kLow, 86cb93a386Sopenharmony_ci kMedium, 87cb93a386Sopenharmony_ci kHigh, 88cb93a386Sopenharmony_ci // this is the special value for backward compatibility 89cb93a386Sopenharmony_ci kInheritFromPaint, 90cb93a386Sopenharmony_ci // this signals we should use the new SkFilterOptions 91cb93a386Sopenharmony_ci kUseFilterOptions, 92cb93a386Sopenharmony_ci // use cubic and ignore FilterOptions 93cb93a386Sopenharmony_ci kUseCubicResampler, 94cb93a386Sopenharmony_ci 95cb93a386Sopenharmony_ci kLast = kUseCubicResampler, 96cb93a386Sopenharmony_ci}; 97cb93a386Sopenharmony_ci 98cb93a386Sopenharmony_ci// fClampAsIfUnpremul is always false when constructed through public APIs, 99cb93a386Sopenharmony_ci// so there's no need to read or write it here. 100cb93a386Sopenharmony_ci 101cb93a386Sopenharmony_cisk_sp<SkFlattenable> SkImageShader::CreateProc(SkReadBuffer& buffer) { 102cb93a386Sopenharmony_ci auto tmx = buffer.read32LE<SkTileMode>(SkTileMode::kLastTileMode); 103cb93a386Sopenharmony_ci auto tmy = buffer.read32LE<SkTileMode>(SkTileMode::kLastTileMode); 104cb93a386Sopenharmony_ci 105cb93a386Sopenharmony_ci SkSamplingOptions sampling; 106cb93a386Sopenharmony_ci bool readSampling = true; 107cb93a386Sopenharmony_ci if (buffer.isVersionLT(SkPicturePriv::kNoFilterQualityShaders_Version) && 108cb93a386Sopenharmony_ci !buffer.readBool() /* legacy has_sampling */) 109cb93a386Sopenharmony_ci { 110cb93a386Sopenharmony_ci readSampling = false; 111cb93a386Sopenharmony_ci // we just default to Nearest in sampling 112cb93a386Sopenharmony_ci } 113cb93a386Sopenharmony_ci if (readSampling) { 114cb93a386Sopenharmony_ci sampling = SkSamplingPriv::Read(buffer); 115cb93a386Sopenharmony_ci } 116cb93a386Sopenharmony_ci 117cb93a386Sopenharmony_ci SkMatrix localMatrix; 118cb93a386Sopenharmony_ci buffer.readMatrix(&localMatrix); 119cb93a386Sopenharmony_ci sk_sp<SkImage> img = buffer.readImage(); 120cb93a386Sopenharmony_ci if (!img) { 121cb93a386Sopenharmony_ci return nullptr; 122cb93a386Sopenharmony_ci } 123cb93a386Sopenharmony_ci 124cb93a386Sopenharmony_ci return SkImageShader::Make(std::move(img), tmx, tmy, sampling, &localMatrix); 125cb93a386Sopenharmony_ci} 126cb93a386Sopenharmony_ci 127cb93a386Sopenharmony_civoid SkImageShader::flatten(SkWriteBuffer& buffer) const { 128cb93a386Sopenharmony_ci buffer.writeUInt((unsigned)fTileModeX); 129cb93a386Sopenharmony_ci buffer.writeUInt((unsigned)fTileModeY); 130cb93a386Sopenharmony_ci 131cb93a386Sopenharmony_ci SkSamplingPriv::Write(buffer, fSampling); 132cb93a386Sopenharmony_ci 133cb93a386Sopenharmony_ci buffer.writeMatrix(this->getLocalMatrix()); 134cb93a386Sopenharmony_ci buffer.writeImage(fImage.get()); 135cb93a386Sopenharmony_ci SkASSERT(fClampAsIfUnpremul == false); 136cb93a386Sopenharmony_ci} 137cb93a386Sopenharmony_ci 138cb93a386Sopenharmony_cibool SkImageShader::isOpaque() const { 139cb93a386Sopenharmony_ci return fImage->isOpaque() && 140cb93a386Sopenharmony_ci fTileModeX != SkTileMode::kDecal && fTileModeY != SkTileMode::kDecal; 141cb93a386Sopenharmony_ci} 142cb93a386Sopenharmony_ci 143cb93a386Sopenharmony_ciconstexpr SkCubicResampler kDefaultCubicResampler{1.0f/3, 1.0f/3}; 144cb93a386Sopenharmony_ci 145cb93a386Sopenharmony_cistatic bool is_default_cubic_resampler(SkCubicResampler cubic) { 146cb93a386Sopenharmony_ci return SkScalarNearlyEqual(cubic.B, kDefaultCubicResampler.B) && 147cb93a386Sopenharmony_ci SkScalarNearlyEqual(cubic.C, kDefaultCubicResampler.C); 148cb93a386Sopenharmony_ci} 149cb93a386Sopenharmony_ci 150cb93a386Sopenharmony_ci#ifdef SK_ENABLE_LEGACY_SHADERCONTEXT 151cb93a386Sopenharmony_ci 152cb93a386Sopenharmony_cistatic bool legacy_shader_can_handle(const SkMatrix& inv) { 153cb93a386Sopenharmony_ci SkASSERT(!inv.hasPerspective()); 154cb93a386Sopenharmony_ci 155cb93a386Sopenharmony_ci // Scale+translate methods are always present, but affine might not be. 156cb93a386Sopenharmony_ci if (!SkOpts::S32_alpha_D32_filter_DXDY && !inv.isScaleTranslate()) { 157cb93a386Sopenharmony_ci return false; 158cb93a386Sopenharmony_ci } 159cb93a386Sopenharmony_ci 160cb93a386Sopenharmony_ci // legacy code uses SkFixed 32.32, so ensure the inverse doesn't map device coordinates 161cb93a386Sopenharmony_ci // out of range. 162cb93a386Sopenharmony_ci const SkScalar max_dev_coord = 32767.0f; 163cb93a386Sopenharmony_ci const SkRect src = inv.mapRect(SkRect::MakeWH(max_dev_coord, max_dev_coord)); 164cb93a386Sopenharmony_ci 165cb93a386Sopenharmony_ci // take 1/4 of max signed 32bits so we have room to subtract local values 166cb93a386Sopenharmony_ci const SkScalar max_fixed32dot32 = float(SK_MaxS32) * 0.25f; 167cb93a386Sopenharmony_ci if (!SkRect::MakeLTRB(-max_fixed32dot32, -max_fixed32dot32, 168cb93a386Sopenharmony_ci +max_fixed32dot32, +max_fixed32dot32).contains(src)) { 169cb93a386Sopenharmony_ci return false; 170cb93a386Sopenharmony_ci } 171cb93a386Sopenharmony_ci 172cb93a386Sopenharmony_ci // legacy shader impl should be able to handle these matrices 173cb93a386Sopenharmony_ci return true; 174cb93a386Sopenharmony_ci} 175cb93a386Sopenharmony_ci 176cb93a386Sopenharmony_ciSkShaderBase::Context* SkImageShader::onMakeContext(const ContextRec& rec, 177cb93a386Sopenharmony_ci SkArenaAlloc* alloc) const { 178cb93a386Sopenharmony_ci if (fImage->alphaType() == kUnpremul_SkAlphaType) { 179cb93a386Sopenharmony_ci return nullptr; 180cb93a386Sopenharmony_ci } 181cb93a386Sopenharmony_ci if (fImage->colorType() != kN32_SkColorType) { 182cb93a386Sopenharmony_ci return nullptr; 183cb93a386Sopenharmony_ci } 184cb93a386Sopenharmony_ci if (fTileModeX != fTileModeY) { 185cb93a386Sopenharmony_ci return nullptr; 186cb93a386Sopenharmony_ci } 187cb93a386Sopenharmony_ci if (fTileModeX == SkTileMode::kDecal || fTileModeY == SkTileMode::kDecal) { 188cb93a386Sopenharmony_ci return nullptr; 189cb93a386Sopenharmony_ci } 190cb93a386Sopenharmony_ci 191cb93a386Sopenharmony_ci auto supported = [](const SkSamplingOptions& sampling) { 192cb93a386Sopenharmony_ci const std::tuple<SkFilterMode,SkMipmapMode> supported[] = { 193cb93a386Sopenharmony_ci {SkFilterMode::kNearest, SkMipmapMode::kNone}, // legacy None 194cb93a386Sopenharmony_ci {SkFilterMode::kLinear, SkMipmapMode::kNone}, // legacy Low 195cb93a386Sopenharmony_ci {SkFilterMode::kLinear, SkMipmapMode::kNearest}, // legacy Medium 196cb93a386Sopenharmony_ci }; 197cb93a386Sopenharmony_ci for (auto [f, m] : supported) { 198cb93a386Sopenharmony_ci if (sampling.filter == f && sampling.mipmap == m) { 199cb93a386Sopenharmony_ci return true; 200cb93a386Sopenharmony_ci } 201cb93a386Sopenharmony_ci } 202cb93a386Sopenharmony_ci return false; 203cb93a386Sopenharmony_ci }; 204cb93a386Sopenharmony_ci if (fSampling.useCubic || !supported(fSampling)) { 205cb93a386Sopenharmony_ci return nullptr; 206cb93a386Sopenharmony_ci } 207cb93a386Sopenharmony_ci 208cb93a386Sopenharmony_ci // SkBitmapProcShader stores bitmap coordinates in a 16bit buffer, 209cb93a386Sopenharmony_ci // so it can't handle bitmaps larger than 65535. 210cb93a386Sopenharmony_ci // 211cb93a386Sopenharmony_ci // We back off another bit to 32767 to make small amounts of 212cb93a386Sopenharmony_ci // intermediate math safe, e.g. in 213cb93a386Sopenharmony_ci // 214cb93a386Sopenharmony_ci // SkFixed fx = ...; 215cb93a386Sopenharmony_ci // fx = tile(fx + SK_Fixed1); 216cb93a386Sopenharmony_ci // 217cb93a386Sopenharmony_ci // we want to make sure (fx + SK_Fixed1) never overflows. 218cb93a386Sopenharmony_ci if (fImage-> width() > 32767 || 219cb93a386Sopenharmony_ci fImage->height() > 32767) { 220cb93a386Sopenharmony_ci return nullptr; 221cb93a386Sopenharmony_ci } 222cb93a386Sopenharmony_ci 223cb93a386Sopenharmony_ci SkMatrix inv; 224cb93a386Sopenharmony_ci if (!this->computeTotalInverse(*rec.fMatrix, rec.fLocalMatrix, &inv) || 225cb93a386Sopenharmony_ci !legacy_shader_can_handle(inv)) { 226cb93a386Sopenharmony_ci return nullptr; 227cb93a386Sopenharmony_ci } 228cb93a386Sopenharmony_ci 229cb93a386Sopenharmony_ci if (!rec.isLegacyCompatible(fImage->colorSpace())) { 230cb93a386Sopenharmony_ci return nullptr; 231cb93a386Sopenharmony_ci } 232cb93a386Sopenharmony_ci 233cb93a386Sopenharmony_ci return SkBitmapProcLegacyShader::MakeContext(*this, fTileModeX, fTileModeY, fSampling, 234cb93a386Sopenharmony_ci as_IB(fImage.get()), rec, alloc); 235cb93a386Sopenharmony_ci} 236cb93a386Sopenharmony_ci#endif 237cb93a386Sopenharmony_ci 238cb93a386Sopenharmony_ciSkImage* SkImageShader::onIsAImage(SkMatrix* texM, SkTileMode xy[]) const { 239cb93a386Sopenharmony_ci if (texM) { 240cb93a386Sopenharmony_ci *texM = this->getLocalMatrix(); 241cb93a386Sopenharmony_ci } 242cb93a386Sopenharmony_ci if (xy) { 243cb93a386Sopenharmony_ci xy[0] = fTileModeX; 244cb93a386Sopenharmony_ci xy[1] = fTileModeY; 245cb93a386Sopenharmony_ci } 246cb93a386Sopenharmony_ci return const_cast<SkImage*>(fImage.get()); 247cb93a386Sopenharmony_ci} 248cb93a386Sopenharmony_ci 249cb93a386Sopenharmony_cisk_sp<SkShader> SkImageShader::Make(sk_sp<SkImage> image, 250cb93a386Sopenharmony_ci SkTileMode tmx, SkTileMode tmy, 251cb93a386Sopenharmony_ci const SkSamplingOptions& options, 252cb93a386Sopenharmony_ci const SkMatrix* localMatrix, 253cb93a386Sopenharmony_ci bool clampAsIfUnpremul) { 254cb93a386Sopenharmony_ci auto is_unit = [](float x) { 255cb93a386Sopenharmony_ci return x >= 0 && x <= 1; 256cb93a386Sopenharmony_ci }; 257cb93a386Sopenharmony_ci if (options.useCubic) { 258cb93a386Sopenharmony_ci if (!is_unit(options.cubic.B) || !is_unit(options.cubic.C)) { 259cb93a386Sopenharmony_ci return nullptr; 260cb93a386Sopenharmony_ci } 261cb93a386Sopenharmony_ci } 262cb93a386Sopenharmony_ci if (!image) { 263cb93a386Sopenharmony_ci return sk_make_sp<SkEmptyShader>(); 264cb93a386Sopenharmony_ci } 265cb93a386Sopenharmony_ci return sk_sp<SkShader>{ 266cb93a386Sopenharmony_ci new SkImageShader(image, tmx, tmy, options, localMatrix, clampAsIfUnpremul) 267cb93a386Sopenharmony_ci }; 268cb93a386Sopenharmony_ci} 269cb93a386Sopenharmony_ci 270cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////////////////////////// 271cb93a386Sopenharmony_ci 272cb93a386Sopenharmony_ci#if SK_SUPPORT_GPU 273cb93a386Sopenharmony_ci 274cb93a386Sopenharmony_ci#include "src/gpu/GrColorInfo.h" 275cb93a386Sopenharmony_ci#include "src/gpu/effects/GrBlendFragmentProcessor.h" 276cb93a386Sopenharmony_ci 277cb93a386Sopenharmony_cistd::unique_ptr<GrFragmentProcessor> SkImageShader::asFragmentProcessor( 278cb93a386Sopenharmony_ci const GrFPArgs& args) const { 279cb93a386Sopenharmony_ci const auto lm = this->totalLocalMatrix(args.fPreLocalMatrix); 280cb93a386Sopenharmony_ci SkMatrix lmInverse; 281cb93a386Sopenharmony_ci if (!lm->invert(&lmInverse)) { 282cb93a386Sopenharmony_ci return nullptr; 283cb93a386Sopenharmony_ci } 284cb93a386Sopenharmony_ci 285cb93a386Sopenharmony_ci SkTileMode tileModes[2] = {fTileModeX, fTileModeY}; 286cb93a386Sopenharmony_ci auto fp = as_IB(fImage.get())->asFragmentProcessor(args.fContext, 287cb93a386Sopenharmony_ci fSampling, 288cb93a386Sopenharmony_ci tileModes, 289cb93a386Sopenharmony_ci lmInverse); 290cb93a386Sopenharmony_ci if (!fp) { 291cb93a386Sopenharmony_ci return nullptr; 292cb93a386Sopenharmony_ci } 293cb93a386Sopenharmony_ci 294cb93a386Sopenharmony_ci fp = GrColorSpaceXformEffect::Make(std::move(fp), 295cb93a386Sopenharmony_ci fImage->colorSpace(), 296cb93a386Sopenharmony_ci fImage->alphaType(), 297cb93a386Sopenharmony_ci args.fDstColorInfo->colorSpace(), 298cb93a386Sopenharmony_ci kPremul_SkAlphaType); 299cb93a386Sopenharmony_ci if (fImage->isAlphaOnly()) { 300cb93a386Sopenharmony_ci return GrBlendFragmentProcessor::Make(std::move(fp), nullptr, SkBlendMode::kDstIn); 301cb93a386Sopenharmony_ci } else { 302cb93a386Sopenharmony_ci return fp; 303cb93a386Sopenharmony_ci } 304cb93a386Sopenharmony_ci} 305cb93a386Sopenharmony_ci 306cb93a386Sopenharmony_ci#endif 307cb93a386Sopenharmony_ci 308cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////////////////////////// 309cb93a386Sopenharmony_ci#include "src/core/SkImagePriv.h" 310cb93a386Sopenharmony_ci 311cb93a386Sopenharmony_cisk_sp<SkShader> SkMakeBitmapShaderForPaint(const SkPaint& paint, const SkBitmap& src, 312cb93a386Sopenharmony_ci SkTileMode tmx, SkTileMode tmy, 313cb93a386Sopenharmony_ci const SkSamplingOptions& sampling, 314cb93a386Sopenharmony_ci const SkMatrix* localMatrix, SkCopyPixelsMode mode) { 315cb93a386Sopenharmony_ci auto s = SkImageShader::Make(SkMakeImageFromRasterBitmap(src, mode), 316cb93a386Sopenharmony_ci tmx, tmy, sampling, localMatrix); 317cb93a386Sopenharmony_ci if (!s) { 318cb93a386Sopenharmony_ci return nullptr; 319cb93a386Sopenharmony_ci } 320cb93a386Sopenharmony_ci if (src.colorType() == kAlpha_8_SkColorType && paint.getShader()) { 321cb93a386Sopenharmony_ci // Compose the image shader with the paint's shader. Alpha images+shaders should output the 322cb93a386Sopenharmony_ci // texture's alpha multiplied by the shader's color. DstIn (d*sa) will achieve this with 323cb93a386Sopenharmony_ci // the source image and dst shader (MakeBlend takes dst first, src second). 324cb93a386Sopenharmony_ci s = SkShaders::Blend(SkBlendMode::kDstIn, paint.refShader(), std::move(s)); 325cb93a386Sopenharmony_ci } 326cb93a386Sopenharmony_ci return s; 327cb93a386Sopenharmony_ci} 328cb93a386Sopenharmony_ci 329cb93a386Sopenharmony_civoid SkShaderBase::RegisterFlattenables() { SK_REGISTER_FLATTENABLE(SkImageShader); } 330cb93a386Sopenharmony_ci 331cb93a386Sopenharmony_ciclass SkImageShader::TransformShader : public SkTransformShader { 332cb93a386Sopenharmony_cipublic: 333cb93a386Sopenharmony_ci explicit TransformShader(const SkImageShader& shader) 334cb93a386Sopenharmony_ci : SkTransformShader{shader} 335cb93a386Sopenharmony_ci , fImageShader{shader} {} 336cb93a386Sopenharmony_ci 337cb93a386Sopenharmony_ci skvm::Color onProgram(skvm::Builder* b, 338cb93a386Sopenharmony_ci skvm::Coord device, skvm::Coord local, skvm::Color color, 339cb93a386Sopenharmony_ci const SkMatrixProvider& matrices, const SkMatrix* localM, 340cb93a386Sopenharmony_ci const SkColorInfo& dst, 341cb93a386Sopenharmony_ci skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override { 342cb93a386Sopenharmony_ci return fImageShader.makeProgram( 343cb93a386Sopenharmony_ci b, device, local, color, matrices, localM, dst, uniforms, this, alloc); 344cb93a386Sopenharmony_ci } 345cb93a386Sopenharmony_ci 346cb93a386Sopenharmony_ciprivate: 347cb93a386Sopenharmony_ci const SkImageShader& fImageShader; 348cb93a386Sopenharmony_ci}; 349cb93a386Sopenharmony_ci 350cb93a386Sopenharmony_cistatic SkSamplingOptions tweak_sampling(SkSamplingOptions sampling, const SkMatrix& matrix) { 351cb93a386Sopenharmony_ci SkFilterMode filter = sampling.filter; 352cb93a386Sopenharmony_ci 353cb93a386Sopenharmony_ci // When the matrix is just an integer translate, bilerp == nearest neighbor. 354cb93a386Sopenharmony_ci if (filter == SkFilterMode::kLinear && 355cb93a386Sopenharmony_ci matrix.getType() <= SkMatrix::kTranslate_Mask && 356cb93a386Sopenharmony_ci matrix.getTranslateX() == (int)matrix.getTranslateX() && 357cb93a386Sopenharmony_ci matrix.getTranslateY() == (int)matrix.getTranslateY()) { 358cb93a386Sopenharmony_ci filter = SkFilterMode::kNearest; 359cb93a386Sopenharmony_ci } 360cb93a386Sopenharmony_ci 361cb93a386Sopenharmony_ci return SkSamplingOptions(filter, sampling.mipmap); 362cb93a386Sopenharmony_ci} 363cb93a386Sopenharmony_ci 364cb93a386Sopenharmony_cistatic SkMatrix tweak_inv_matrix(SkFilterMode filter, SkMatrix matrix) { 365cb93a386Sopenharmony_ci // See skia:4649 and the GM image_scale_aligned. 366cb93a386Sopenharmony_ci if (filter == SkFilterMode::kNearest) { 367cb93a386Sopenharmony_ci if (matrix.getScaleX() >= 0) { 368cb93a386Sopenharmony_ci matrix.setTranslateX(nextafterf(matrix.getTranslateX(), 369cb93a386Sopenharmony_ci floorf(matrix.getTranslateX()))); 370cb93a386Sopenharmony_ci } 371cb93a386Sopenharmony_ci if (matrix.getScaleY() >= 0) { 372cb93a386Sopenharmony_ci matrix.setTranslateY(nextafterf(matrix.getTranslateY(), 373cb93a386Sopenharmony_ci floorf(matrix.getTranslateY()))); 374cb93a386Sopenharmony_ci } 375cb93a386Sopenharmony_ci } 376cb93a386Sopenharmony_ci return matrix; 377cb93a386Sopenharmony_ci} 378cb93a386Sopenharmony_ci 379cb93a386Sopenharmony_cibool SkImageShader::doStages(const SkStageRec& rec, TransformShader* updater) const { 380cb93a386Sopenharmony_ci // We only support certain sampling options in stages so far 381cb93a386Sopenharmony_ci auto sampling = fSampling; 382cb93a386Sopenharmony_ci if (sampling.useCubic) { 383cb93a386Sopenharmony_ci if (!is_default_cubic_resampler(sampling.cubic)) { 384cb93a386Sopenharmony_ci return false; 385cb93a386Sopenharmony_ci } 386cb93a386Sopenharmony_ci } else if (sampling.mipmap == SkMipmapMode::kLinear) { 387cb93a386Sopenharmony_ci return false; 388cb93a386Sopenharmony_ci } 389cb93a386Sopenharmony_ci 390cb93a386Sopenharmony_ci 391cb93a386Sopenharmony_ci if (updater && (sampling.mipmap != SkMipmapMode::kNone)) { 392cb93a386Sopenharmony_ci // TODO: medium: recall RequestBitmap and update width/height accordingly 393cb93a386Sopenharmony_ci return false; 394cb93a386Sopenharmony_ci } 395cb93a386Sopenharmony_ci 396cb93a386Sopenharmony_ci SkRasterPipeline* p = rec.fPipeline; 397cb93a386Sopenharmony_ci SkArenaAlloc* alloc = rec.fAlloc; 398cb93a386Sopenharmony_ci 399cb93a386Sopenharmony_ci SkMatrix matrix; 400cb93a386Sopenharmony_ci if (!this->computeTotalInverse(rec.fMatrixProvider.localToDevice(), rec.fLocalM, &matrix)) { 401cb93a386Sopenharmony_ci return false; 402cb93a386Sopenharmony_ci } 403cb93a386Sopenharmony_ci matrix.normalizePerspective(); 404cb93a386Sopenharmony_ci 405cb93a386Sopenharmony_ci SkASSERT(!sampling.useCubic || sampling.mipmap == SkMipmapMode::kNone); 406cb93a386Sopenharmony_ci auto* access = SkMipmapAccessor::Make(alloc, fImage.get(), matrix, sampling.mipmap); 407cb93a386Sopenharmony_ci if (!access) { 408cb93a386Sopenharmony_ci return false; 409cb93a386Sopenharmony_ci } 410cb93a386Sopenharmony_ci SkPixmap pm; 411cb93a386Sopenharmony_ci std::tie(pm, matrix) = access->level(); 412cb93a386Sopenharmony_ci 413cb93a386Sopenharmony_ci p->append(SkRasterPipeline::seed_shader); 414cb93a386Sopenharmony_ci 415cb93a386Sopenharmony_ci if (updater) { 416cb93a386Sopenharmony_ci updater->appendMatrix(rec.fMatrixProvider.localToDevice(), p); 417cb93a386Sopenharmony_ci } else { 418cb93a386Sopenharmony_ci if (!sampling.useCubic) { 419cb93a386Sopenharmony_ci // TODO: can tweak_sampling sometimes for cubic too when B=0 420cb93a386Sopenharmony_ci if (rec.fMatrixProvider.localToDeviceHitsPixelCenters()) { 421cb93a386Sopenharmony_ci sampling = tweak_sampling(sampling, matrix); 422cb93a386Sopenharmony_ci } 423cb93a386Sopenharmony_ci matrix = tweak_inv_matrix(sampling.filter, matrix); 424cb93a386Sopenharmony_ci } 425cb93a386Sopenharmony_ci p->append_matrix(alloc, matrix); 426cb93a386Sopenharmony_ci } 427cb93a386Sopenharmony_ci 428cb93a386Sopenharmony_ci auto gather = alloc->make<SkRasterPipeline_GatherCtx>(); 429cb93a386Sopenharmony_ci gather->pixels = pm.addr(); 430cb93a386Sopenharmony_ci gather->stride = pm.rowBytesAsPixels(); 431cb93a386Sopenharmony_ci gather->width = pm.width(); 432cb93a386Sopenharmony_ci gather->height = pm.height(); 433cb93a386Sopenharmony_ci 434cb93a386Sopenharmony_ci auto limit_x = alloc->make<SkRasterPipeline_TileCtx>(), 435cb93a386Sopenharmony_ci limit_y = alloc->make<SkRasterPipeline_TileCtx>(); 436cb93a386Sopenharmony_ci limit_x->scale = pm.width(); 437cb93a386Sopenharmony_ci limit_x->invScale = 1.0f / pm.width(); 438cb93a386Sopenharmony_ci limit_y->scale = pm.height(); 439cb93a386Sopenharmony_ci limit_y->invScale = 1.0f / pm.height(); 440cb93a386Sopenharmony_ci 441cb93a386Sopenharmony_ci SkRasterPipeline_DecalTileCtx* decal_ctx = nullptr; 442cb93a386Sopenharmony_ci bool decal_x_and_y = fTileModeX == SkTileMode::kDecal && fTileModeY == SkTileMode::kDecal; 443cb93a386Sopenharmony_ci if (fTileModeX == SkTileMode::kDecal || fTileModeY == SkTileMode::kDecal) { 444cb93a386Sopenharmony_ci decal_ctx = alloc->make<SkRasterPipeline_DecalTileCtx>(); 445cb93a386Sopenharmony_ci decal_ctx->limit_x = limit_x->scale; 446cb93a386Sopenharmony_ci decal_ctx->limit_y = limit_y->scale; 447cb93a386Sopenharmony_ci } 448cb93a386Sopenharmony_ci 449cb93a386Sopenharmony_ci auto append_tiling_and_gather = [&] { 450cb93a386Sopenharmony_ci if (decal_x_and_y) { 451cb93a386Sopenharmony_ci p->append(SkRasterPipeline::decal_x_and_y, decal_ctx); 452cb93a386Sopenharmony_ci } else { 453cb93a386Sopenharmony_ci switch (fTileModeX) { 454cb93a386Sopenharmony_ci case SkTileMode::kClamp: /* The gather_xxx stage will clamp for us. */ break; 455cb93a386Sopenharmony_ci case SkTileMode::kMirror: p->append(SkRasterPipeline::mirror_x, limit_x); break; 456cb93a386Sopenharmony_ci case SkTileMode::kRepeat: p->append(SkRasterPipeline::repeat_x, limit_x); break; 457cb93a386Sopenharmony_ci case SkTileMode::kDecal: p->append(SkRasterPipeline::decal_x, decal_ctx); break; 458cb93a386Sopenharmony_ci } 459cb93a386Sopenharmony_ci switch (fTileModeY) { 460cb93a386Sopenharmony_ci case SkTileMode::kClamp: /* The gather_xxx stage will clamp for us. */ break; 461cb93a386Sopenharmony_ci case SkTileMode::kMirror: p->append(SkRasterPipeline::mirror_y, limit_y); break; 462cb93a386Sopenharmony_ci case SkTileMode::kRepeat: p->append(SkRasterPipeline::repeat_y, limit_y); break; 463cb93a386Sopenharmony_ci case SkTileMode::kDecal: p->append(SkRasterPipeline::decal_y, decal_ctx); break; 464cb93a386Sopenharmony_ci } 465cb93a386Sopenharmony_ci } 466cb93a386Sopenharmony_ci 467cb93a386Sopenharmony_ci void* ctx = gather; 468cb93a386Sopenharmony_ci switch (pm.colorType()) { 469cb93a386Sopenharmony_ci case kAlpha_8_SkColorType: p->append(SkRasterPipeline::gather_a8, ctx); break; 470cb93a386Sopenharmony_ci case kA16_unorm_SkColorType: p->append(SkRasterPipeline::gather_a16, ctx); break; 471cb93a386Sopenharmony_ci case kA16_float_SkColorType: p->append(SkRasterPipeline::gather_af16, ctx); break; 472cb93a386Sopenharmony_ci case kRGB_565_SkColorType: p->append(SkRasterPipeline::gather_565, ctx); break; 473cb93a386Sopenharmony_ci case kARGB_4444_SkColorType: p->append(SkRasterPipeline::gather_4444, ctx); break; 474cb93a386Sopenharmony_ci case kR8G8_unorm_SkColorType: p->append(SkRasterPipeline::gather_rg88, ctx); break; 475cb93a386Sopenharmony_ci case kR16G16_unorm_SkColorType: p->append(SkRasterPipeline::gather_rg1616, ctx); break; 476cb93a386Sopenharmony_ci case kR16G16_float_SkColorType: p->append(SkRasterPipeline::gather_rgf16, ctx); break; 477cb93a386Sopenharmony_ci case kRGBA_8888_SkColorType: p->append(SkRasterPipeline::gather_8888, ctx); break; 478cb93a386Sopenharmony_ci case kRGBA_1010102_SkColorType: p->append(SkRasterPipeline::gather_1010102, ctx); break; 479cb93a386Sopenharmony_ci case kR16G16B16A16_unorm_SkColorType: 480cb93a386Sopenharmony_ci p->append(SkRasterPipeline::gather_16161616,ctx); break; 481cb93a386Sopenharmony_ci case kRGBA_F16Norm_SkColorType: 482cb93a386Sopenharmony_ci case kRGBA_F16_SkColorType: p->append(SkRasterPipeline::gather_f16, ctx); break; 483cb93a386Sopenharmony_ci case kRGBA_F32_SkColorType: p->append(SkRasterPipeline::gather_f32, ctx); break; 484cb93a386Sopenharmony_ci 485cb93a386Sopenharmony_ci case kGray_8_SkColorType: p->append(SkRasterPipeline::gather_a8, ctx); 486cb93a386Sopenharmony_ci p->append(SkRasterPipeline::alpha_to_gray ); break; 487cb93a386Sopenharmony_ci 488cb93a386Sopenharmony_ci case kRGB_888x_SkColorType: p->append(SkRasterPipeline::gather_8888, ctx); 489cb93a386Sopenharmony_ci p->append(SkRasterPipeline::force_opaque ); break; 490cb93a386Sopenharmony_ci 491cb93a386Sopenharmony_ci case kBGRA_1010102_SkColorType: p->append(SkRasterPipeline::gather_1010102, ctx); 492cb93a386Sopenharmony_ci p->append(SkRasterPipeline::swap_rb ); break; 493cb93a386Sopenharmony_ci 494cb93a386Sopenharmony_ci case kRGB_101010x_SkColorType: p->append(SkRasterPipeline::gather_1010102, ctx); 495cb93a386Sopenharmony_ci p->append(SkRasterPipeline::force_opaque ); break; 496cb93a386Sopenharmony_ci 497cb93a386Sopenharmony_ci case kBGR_101010x_SkColorType: p->append(SkRasterPipeline::gather_1010102, ctx); 498cb93a386Sopenharmony_ci p->append(SkRasterPipeline::force_opaque ); 499cb93a386Sopenharmony_ci p->append(SkRasterPipeline::swap_rb ); break; 500cb93a386Sopenharmony_ci 501cb93a386Sopenharmony_ci case kBGRA_8888_SkColorType: p->append(SkRasterPipeline::gather_8888, ctx); 502cb93a386Sopenharmony_ci p->append(SkRasterPipeline::swap_rb ); break; 503cb93a386Sopenharmony_ci 504cb93a386Sopenharmony_ci case kSRGBA_8888_SkColorType: 505cb93a386Sopenharmony_ci p->append(SkRasterPipeline::gather_8888, ctx); 506cb93a386Sopenharmony_ci p->append_transfer_function(*skcms_sRGB_TransferFunction()); 507cb93a386Sopenharmony_ci break; 508cb93a386Sopenharmony_ci 509cb93a386Sopenharmony_ci case kUnknown_SkColorType: SkASSERT(false); 510cb93a386Sopenharmony_ci } 511cb93a386Sopenharmony_ci if (decal_ctx) { 512cb93a386Sopenharmony_ci p->append(SkRasterPipeline::check_decal_mask, decal_ctx); 513cb93a386Sopenharmony_ci } 514cb93a386Sopenharmony_ci }; 515cb93a386Sopenharmony_ci 516cb93a386Sopenharmony_ci auto append_misc = [&] { 517cb93a386Sopenharmony_ci SkColorSpace* cs = pm.colorSpace(); 518cb93a386Sopenharmony_ci SkAlphaType at = pm.alphaType(); 519cb93a386Sopenharmony_ci 520cb93a386Sopenharmony_ci // Color for A8 images comes from the paint. TODO: all alpha images? none? 521cb93a386Sopenharmony_ci if (pm.colorType() == kAlpha_8_SkColorType) { 522cb93a386Sopenharmony_ci SkColor4f rgb = rec.fPaint.getColor4f(); 523cb93a386Sopenharmony_ci p->append_set_rgb(alloc, rgb); 524cb93a386Sopenharmony_ci 525cb93a386Sopenharmony_ci cs = sk_srgb_singleton(); 526cb93a386Sopenharmony_ci at = kUnpremul_SkAlphaType; 527cb93a386Sopenharmony_ci } 528cb93a386Sopenharmony_ci 529cb93a386Sopenharmony_ci // Bicubic filtering naturally produces out of range values on both sides of [0,1]. 530cb93a386Sopenharmony_ci if (sampling.useCubic) { 531cb93a386Sopenharmony_ci p->append(SkRasterPipeline::clamp_0); 532cb93a386Sopenharmony_ci p->append(at == kUnpremul_SkAlphaType || fClampAsIfUnpremul 533cb93a386Sopenharmony_ci ? SkRasterPipeline::clamp_1 534cb93a386Sopenharmony_ci : SkRasterPipeline::clamp_a); 535cb93a386Sopenharmony_ci } 536cb93a386Sopenharmony_ci 537cb93a386Sopenharmony_ci // Transform color space and alpha type to match shader convention (dst CS, premul alpha). 538cb93a386Sopenharmony_ci alloc->make<SkColorSpaceXformSteps>(cs, at, 539cb93a386Sopenharmony_ci rec.fDstCS, kPremul_SkAlphaType) 540cb93a386Sopenharmony_ci ->apply(p); 541cb93a386Sopenharmony_ci 542cb93a386Sopenharmony_ci return true; 543cb93a386Sopenharmony_ci }; 544cb93a386Sopenharmony_ci 545cb93a386Sopenharmony_ci // Check for fast-path stages. 546cb93a386Sopenharmony_ci auto ct = pm.colorType(); 547cb93a386Sopenharmony_ci if (true 548cb93a386Sopenharmony_ci && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType) 549cb93a386Sopenharmony_ci && !sampling.useCubic && sampling.filter == SkFilterMode::kLinear 550cb93a386Sopenharmony_ci && fTileModeX == SkTileMode::kClamp && fTileModeY == SkTileMode::kClamp) { 551cb93a386Sopenharmony_ci 552cb93a386Sopenharmony_ci p->append(SkRasterPipeline::bilerp_clamp_8888, gather); 553cb93a386Sopenharmony_ci if (ct == kBGRA_8888_SkColorType) { 554cb93a386Sopenharmony_ci p->append(SkRasterPipeline::swap_rb); 555cb93a386Sopenharmony_ci } 556cb93a386Sopenharmony_ci return append_misc(); 557cb93a386Sopenharmony_ci } 558cb93a386Sopenharmony_ci if (true 559cb93a386Sopenharmony_ci && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType) // TODO: all formats 560cb93a386Sopenharmony_ci && !sampling.useCubic && sampling.filter == SkFilterMode::kLinear 561cb93a386Sopenharmony_ci && fTileModeX != SkTileMode::kDecal // TODO decal too? 562cb93a386Sopenharmony_ci && fTileModeY != SkTileMode::kDecal) { 563cb93a386Sopenharmony_ci 564cb93a386Sopenharmony_ci auto ctx = alloc->make<SkRasterPipeline_SamplerCtx2>(); 565cb93a386Sopenharmony_ci *(SkRasterPipeline_GatherCtx*)(ctx) = *gather; 566cb93a386Sopenharmony_ci ctx->ct = ct; 567cb93a386Sopenharmony_ci ctx->tileX = fTileModeX; 568cb93a386Sopenharmony_ci ctx->tileY = fTileModeY; 569cb93a386Sopenharmony_ci ctx->invWidth = 1.0f / ctx->width; 570cb93a386Sopenharmony_ci ctx->invHeight = 1.0f / ctx->height; 571cb93a386Sopenharmony_ci p->append(SkRasterPipeline::bilinear, ctx); 572cb93a386Sopenharmony_ci return append_misc(); 573cb93a386Sopenharmony_ci } 574cb93a386Sopenharmony_ci if (true 575cb93a386Sopenharmony_ci && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType) 576cb93a386Sopenharmony_ci && sampling.useCubic 577cb93a386Sopenharmony_ci && fTileModeX == SkTileMode::kClamp && fTileModeY == SkTileMode::kClamp) { 578cb93a386Sopenharmony_ci 579cb93a386Sopenharmony_ci p->append(SkRasterPipeline::bicubic_clamp_8888, gather); 580cb93a386Sopenharmony_ci if (ct == kBGRA_8888_SkColorType) { 581cb93a386Sopenharmony_ci p->append(SkRasterPipeline::swap_rb); 582cb93a386Sopenharmony_ci } 583cb93a386Sopenharmony_ci return append_misc(); 584cb93a386Sopenharmony_ci } 585cb93a386Sopenharmony_ci if (true 586cb93a386Sopenharmony_ci && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType) // TODO: all formats 587cb93a386Sopenharmony_ci && sampling.useCubic 588cb93a386Sopenharmony_ci && fTileModeX != SkTileMode::kDecal // TODO decal too? 589cb93a386Sopenharmony_ci && fTileModeY != SkTileMode::kDecal) { 590cb93a386Sopenharmony_ci 591cb93a386Sopenharmony_ci auto ctx = alloc->make<SkRasterPipeline_SamplerCtx2>(); 592cb93a386Sopenharmony_ci *(SkRasterPipeline_GatherCtx*)(ctx) = *gather; 593cb93a386Sopenharmony_ci ctx->ct = ct; 594cb93a386Sopenharmony_ci ctx->tileX = fTileModeX; 595cb93a386Sopenharmony_ci ctx->tileY = fTileModeY; 596cb93a386Sopenharmony_ci ctx->invWidth = 1.0f / ctx->width; 597cb93a386Sopenharmony_ci ctx->invHeight = 1.0f / ctx->height; 598cb93a386Sopenharmony_ci p->append(SkRasterPipeline::bicubic, ctx); 599cb93a386Sopenharmony_ci return append_misc(); 600cb93a386Sopenharmony_ci } 601cb93a386Sopenharmony_ci 602cb93a386Sopenharmony_ci SkRasterPipeline_SamplerCtx* sampler = alloc->make<SkRasterPipeline_SamplerCtx>(); 603cb93a386Sopenharmony_ci 604cb93a386Sopenharmony_ci auto sample = [&](SkRasterPipeline::StockStage setup_x, 605cb93a386Sopenharmony_ci SkRasterPipeline::StockStage setup_y) { 606cb93a386Sopenharmony_ci p->append(setup_x, sampler); 607cb93a386Sopenharmony_ci p->append(setup_y, sampler); 608cb93a386Sopenharmony_ci append_tiling_and_gather(); 609cb93a386Sopenharmony_ci p->append(SkRasterPipeline::accumulate, sampler); 610cb93a386Sopenharmony_ci }; 611cb93a386Sopenharmony_ci 612cb93a386Sopenharmony_ci if (sampling.useCubic) { 613cb93a386Sopenharmony_ci p->append(SkRasterPipeline::save_xy, sampler); 614cb93a386Sopenharmony_ci 615cb93a386Sopenharmony_ci sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n3y); 616cb93a386Sopenharmony_ci sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n3y); 617cb93a386Sopenharmony_ci sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n3y); 618cb93a386Sopenharmony_ci sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n3y); 619cb93a386Sopenharmony_ci 620cb93a386Sopenharmony_ci sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n1y); 621cb93a386Sopenharmony_ci sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n1y); 622cb93a386Sopenharmony_ci sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n1y); 623cb93a386Sopenharmony_ci sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n1y); 624cb93a386Sopenharmony_ci 625cb93a386Sopenharmony_ci sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p1y); 626cb93a386Sopenharmony_ci sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p1y); 627cb93a386Sopenharmony_ci sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p1y); 628cb93a386Sopenharmony_ci sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p1y); 629cb93a386Sopenharmony_ci 630cb93a386Sopenharmony_ci sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p3y); 631cb93a386Sopenharmony_ci sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p3y); 632cb93a386Sopenharmony_ci sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p3y); 633cb93a386Sopenharmony_ci sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p3y); 634cb93a386Sopenharmony_ci 635cb93a386Sopenharmony_ci p->append(SkRasterPipeline::move_dst_src); 636cb93a386Sopenharmony_ci } else if (sampling.filter == SkFilterMode::kLinear) { 637cb93a386Sopenharmony_ci p->append(SkRasterPipeline::save_xy, sampler); 638cb93a386Sopenharmony_ci 639cb93a386Sopenharmony_ci sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_ny); 640cb93a386Sopenharmony_ci sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_ny); 641cb93a386Sopenharmony_ci sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_py); 642cb93a386Sopenharmony_ci sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_py); 643cb93a386Sopenharmony_ci 644cb93a386Sopenharmony_ci p->append(SkRasterPipeline::move_dst_src); 645cb93a386Sopenharmony_ci } else { 646cb93a386Sopenharmony_ci append_tiling_and_gather(); 647cb93a386Sopenharmony_ci } 648cb93a386Sopenharmony_ci 649cb93a386Sopenharmony_ci return append_misc(); 650cb93a386Sopenharmony_ci} 651cb93a386Sopenharmony_ci 652cb93a386Sopenharmony_cibool SkImageShader::onAppendStages(const SkStageRec& rec) const { 653cb93a386Sopenharmony_ci return this->doStages(rec, nullptr); 654cb93a386Sopenharmony_ci} 655cb93a386Sopenharmony_ci 656cb93a386Sopenharmony_ciSkStageUpdater* SkImageShader::onAppendUpdatableStages(const SkStageRec& rec) const { 657cb93a386Sopenharmony_ci TransformShader* updater = rec.fAlloc->make<TransformShader>(*this); 658cb93a386Sopenharmony_ci return this->doStages(rec, updater) ? updater : nullptr; 659cb93a386Sopenharmony_ci} 660cb93a386Sopenharmony_ci 661cb93a386Sopenharmony_ciSkUpdatableShader* SkImageShader::onUpdatableShader(SkArenaAlloc* alloc) const { 662cb93a386Sopenharmony_ci return alloc->make<TransformShader>(*this); 663cb93a386Sopenharmony_ci} 664cb93a386Sopenharmony_ci 665cb93a386Sopenharmony_ciskvm::Color SkImageShader::onProgram(skvm::Builder* b, 666cb93a386Sopenharmony_ci skvm::Coord device, skvm::Coord origLocal, skvm::Color paint, 667cb93a386Sopenharmony_ci const SkMatrixProvider& matrices, const SkMatrix* localM, 668cb93a386Sopenharmony_ci const SkColorInfo& dst, 669cb93a386Sopenharmony_ci skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const { 670cb93a386Sopenharmony_ci return this->makeProgram( 671cb93a386Sopenharmony_ci b, device, origLocal, paint, matrices, localM, dst, uniforms, nullptr, alloc); 672cb93a386Sopenharmony_ci} 673cb93a386Sopenharmony_ci 674cb93a386Sopenharmony_ciskvm::Color SkImageShader::makeProgram( 675cb93a386Sopenharmony_ci skvm::Builder* p, skvm::Coord device, skvm::Coord origLocal, skvm::Color paint, 676cb93a386Sopenharmony_ci const SkMatrixProvider& matrices, const SkMatrix* localM, const SkColorInfo& dst, 677cb93a386Sopenharmony_ci skvm::Uniforms* uniforms, const TransformShader* coordShader, SkArenaAlloc* alloc) const { 678cb93a386Sopenharmony_ci 679cb93a386Sopenharmony_ci SkMatrix baseInv; 680cb93a386Sopenharmony_ci if (!this->computeTotalInverse(matrices.localToDevice(), localM, &baseInv)) { 681cb93a386Sopenharmony_ci return {}; 682cb93a386Sopenharmony_ci } 683cb93a386Sopenharmony_ci baseInv.normalizePerspective(); 684cb93a386Sopenharmony_ci 685cb93a386Sopenharmony_ci auto sampling = fSampling; 686cb93a386Sopenharmony_ci auto* access = SkMipmapAccessor::Make(alloc, fImage.get(), baseInv, sampling.mipmap); 687cb93a386Sopenharmony_ci if (!access) { 688cb93a386Sopenharmony_ci return {}; 689cb93a386Sopenharmony_ci } 690cb93a386Sopenharmony_ci auto [upper, upperInv] = access->level(); 691cb93a386Sopenharmony_ci // If we are using a coordShader, then we can't make guesses about the state of the matrix. 692cb93a386Sopenharmony_ci if (!sampling.useCubic && !coordShader) { 693cb93a386Sopenharmony_ci // TODO: can tweak_sampling sometimes for cubic too when B=0 694cb93a386Sopenharmony_ci if (matrices.localToDeviceHitsPixelCenters()) { 695cb93a386Sopenharmony_ci sampling = tweak_sampling(sampling, upperInv); 696cb93a386Sopenharmony_ci } 697cb93a386Sopenharmony_ci upperInv = tweak_inv_matrix(sampling.filter, upperInv); 698cb93a386Sopenharmony_ci } 699cb93a386Sopenharmony_ci 700cb93a386Sopenharmony_ci SkPixmap lowerPixmap; 701cb93a386Sopenharmony_ci SkMatrix lowerInv; 702cb93a386Sopenharmony_ci SkPixmap* lower = nullptr; 703cb93a386Sopenharmony_ci float lowerWeight = access->lowerWeight(); 704cb93a386Sopenharmony_ci if (lowerWeight > 0) { 705cb93a386Sopenharmony_ci std::tie(lowerPixmap, lowerInv) = access->lowerLevel(); 706cb93a386Sopenharmony_ci lower = &lowerPixmap; 707cb93a386Sopenharmony_ci } 708cb93a386Sopenharmony_ci 709cb93a386Sopenharmony_ci skvm::Coord upperLocal; 710cb93a386Sopenharmony_ci if (coordShader != nullptr) { 711cb93a386Sopenharmony_ci upperLocal = coordShader->applyMatrix(p, upperInv, origLocal, uniforms); 712cb93a386Sopenharmony_ci } else { 713cb93a386Sopenharmony_ci upperLocal = SkShaderBase::ApplyMatrix(p, upperInv, origLocal, uniforms); 714cb93a386Sopenharmony_ci } 715cb93a386Sopenharmony_ci 716cb93a386Sopenharmony_ci // We can exploit image opacity to skip work unpacking alpha channels. 717cb93a386Sopenharmony_ci const bool input_is_opaque = SkAlphaTypeIsOpaque(upper.alphaType()) 718cb93a386Sopenharmony_ci || SkColorTypeIsAlwaysOpaque(upper.colorType()); 719cb93a386Sopenharmony_ci 720cb93a386Sopenharmony_ci // Each call to sample() will try to rewrite the same uniforms over and over, 721cb93a386Sopenharmony_ci // so remember where we start and reset back there each time. That way each 722cb93a386Sopenharmony_ci // sample() call uses the same uniform offsets. 723cb93a386Sopenharmony_ci 724cb93a386Sopenharmony_ci auto compute_clamp_limit = [&](float limit) { 725cb93a386Sopenharmony_ci // Subtract an ulp so the upper clamp limit excludes limit itself. 726cb93a386Sopenharmony_ci int bits; 727cb93a386Sopenharmony_ci memcpy(&bits, &limit, 4); 728cb93a386Sopenharmony_ci return p->uniformF(uniforms->push(bits-1)); 729cb93a386Sopenharmony_ci }; 730cb93a386Sopenharmony_ci 731cb93a386Sopenharmony_ci // Except in the simplest case (no mips, no filtering), we reference uniforms 732cb93a386Sopenharmony_ci // more than once. To avoid adding/registering them multiple times, we pre-load them 733cb93a386Sopenharmony_ci // into a struct (just to logically group them together), based on the "current" 734cb93a386Sopenharmony_ci // pixmap (level of a mipmap). 735cb93a386Sopenharmony_ci // 736cb93a386Sopenharmony_ci struct Uniforms { 737cb93a386Sopenharmony_ci skvm::F32 w, iw, i2w, 738cb93a386Sopenharmony_ci h, ih, i2h; 739cb93a386Sopenharmony_ci 740cb93a386Sopenharmony_ci skvm::F32 clamp_w, 741cb93a386Sopenharmony_ci clamp_h; 742cb93a386Sopenharmony_ci 743cb93a386Sopenharmony_ci skvm::Uniform addr; 744cb93a386Sopenharmony_ci skvm::I32 rowBytesAsPixels; 745cb93a386Sopenharmony_ci 746cb93a386Sopenharmony_ci skvm::PixelFormat pixelFormat; // not a uniform, but needed for each texel sample, 747cb93a386Sopenharmony_ci // so we store it here, since it is also dependent on 748cb93a386Sopenharmony_ci // the current pixmap (level). 749cb93a386Sopenharmony_ci }; 750cb93a386Sopenharmony_ci 751cb93a386Sopenharmony_ci auto setup_uniforms = [&](const SkPixmap& pm) -> Uniforms { 752cb93a386Sopenharmony_ci skvm::PixelFormat pixelFormat = skvm::SkColorType_to_PixelFormat(pm.colorType()); 753cb93a386Sopenharmony_ci return { 754cb93a386Sopenharmony_ci p->uniformF(uniforms->pushF( pm.width())), 755cb93a386Sopenharmony_ci p->uniformF(uniforms->pushF(1.0f/pm.width())), // iff tileX == kRepeat 756cb93a386Sopenharmony_ci p->uniformF(uniforms->pushF(0.5f/pm.width())), // iff tileX == kMirror 757cb93a386Sopenharmony_ci 758cb93a386Sopenharmony_ci p->uniformF(uniforms->pushF( pm.height())), 759cb93a386Sopenharmony_ci p->uniformF(uniforms->pushF(1.0f/pm.height())), // iff tileY == kRepeat 760cb93a386Sopenharmony_ci p->uniformF(uniforms->pushF(0.5f/pm.height())), // iff tileY == kMirror 761cb93a386Sopenharmony_ci 762cb93a386Sopenharmony_ci compute_clamp_limit(pm. width()), 763cb93a386Sopenharmony_ci compute_clamp_limit(pm.height()), 764cb93a386Sopenharmony_ci 765cb93a386Sopenharmony_ci uniforms->pushPtr(pm.addr()), 766cb93a386Sopenharmony_ci p->uniform32(uniforms->push(pm.rowBytesAsPixels())), 767cb93a386Sopenharmony_ci 768cb93a386Sopenharmony_ci pixelFormat, 769cb93a386Sopenharmony_ci }; 770cb93a386Sopenharmony_ci }; 771cb93a386Sopenharmony_ci 772cb93a386Sopenharmony_ci auto sample_texel = [&](const Uniforms& u, skvm::F32 sx, skvm::F32 sy) -> skvm::Color { 773cb93a386Sopenharmony_ci // repeat() and mirror() are written assuming they'll be followed by a [0,scale) clamp. 774cb93a386Sopenharmony_ci auto repeat = [&](skvm::F32 v, skvm::F32 S, skvm::F32 I) { 775cb93a386Sopenharmony_ci return v - floor(v * I) * S; 776cb93a386Sopenharmony_ci }; 777cb93a386Sopenharmony_ci auto mirror = [&](skvm::F32 v, skvm::F32 S, skvm::F32 I2) { 778cb93a386Sopenharmony_ci // abs( (v-scale) - (2*scale)*floor((v-scale)*(0.5f/scale)) - scale ) 779cb93a386Sopenharmony_ci // {---A---} {------------------B------------------} 780cb93a386Sopenharmony_ci skvm::F32 A = v - S, 781cb93a386Sopenharmony_ci B = (S + S) * floor(A * I2); 782cb93a386Sopenharmony_ci return abs(A - B - S); 783cb93a386Sopenharmony_ci }; 784cb93a386Sopenharmony_ci switch (fTileModeX) { 785cb93a386Sopenharmony_ci case SkTileMode::kDecal: /* handled after gather */ break; 786cb93a386Sopenharmony_ci case SkTileMode::kClamp: /* we always clamp */ break; 787cb93a386Sopenharmony_ci case SkTileMode::kRepeat: sx = repeat(sx, u.w, u.iw); break; 788cb93a386Sopenharmony_ci case SkTileMode::kMirror: sx = mirror(sx, u.w, u.i2w); break; 789cb93a386Sopenharmony_ci } 790cb93a386Sopenharmony_ci switch (fTileModeY) { 791cb93a386Sopenharmony_ci case SkTileMode::kDecal: /* handled after gather */ break; 792cb93a386Sopenharmony_ci case SkTileMode::kClamp: /* we always clamp */ break; 793cb93a386Sopenharmony_ci case SkTileMode::kRepeat: sy = repeat(sy, u.h, u.ih); break; 794cb93a386Sopenharmony_ci case SkTileMode::kMirror: sy = mirror(sy, u.h, u.i2h); break; 795cb93a386Sopenharmony_ci } 796cb93a386Sopenharmony_ci 797cb93a386Sopenharmony_ci // Always clamp sample coordinates to [0,width), [0,height), both for memory 798cb93a386Sopenharmony_ci // safety and to handle the clamps still needed by kClamp, kRepeat, and kMirror. 799cb93a386Sopenharmony_ci skvm::F32 clamped_x = clamp(sx, 0, u.clamp_w), 800cb93a386Sopenharmony_ci clamped_y = clamp(sy, 0, u.clamp_h); 801cb93a386Sopenharmony_ci 802cb93a386Sopenharmony_ci // Load pixels from pm.addr()[(int)sx + (int)sy*stride]. 803cb93a386Sopenharmony_ci skvm::I32 index = trunc(clamped_x) + 804cb93a386Sopenharmony_ci trunc(clamped_y) * u.rowBytesAsPixels; 805cb93a386Sopenharmony_ci skvm::Color c = gather(u.pixelFormat, u.addr, index); 806cb93a386Sopenharmony_ci 807cb93a386Sopenharmony_ci // If we know the image is opaque, jump right to alpha = 1.0f, skipping work to unpack it. 808cb93a386Sopenharmony_ci if (input_is_opaque) { 809cb93a386Sopenharmony_ci c.a = p->splat(1.0f); 810cb93a386Sopenharmony_ci } 811cb93a386Sopenharmony_ci 812cb93a386Sopenharmony_ci // Mask away any pixels that we tried to sample outside the bounds in kDecal. 813cb93a386Sopenharmony_ci if (fTileModeX == SkTileMode::kDecal || fTileModeY == SkTileMode::kDecal) { 814cb93a386Sopenharmony_ci skvm::I32 mask = p->splat(~0); 815cb93a386Sopenharmony_ci if (fTileModeX == SkTileMode::kDecal) { mask &= (sx == clamped_x); } 816cb93a386Sopenharmony_ci if (fTileModeY == SkTileMode::kDecal) { mask &= (sy == clamped_y); } 817cb93a386Sopenharmony_ci c.r = pun_to_F32(p->bit_and(mask, pun_to_I32(c.r))); 818cb93a386Sopenharmony_ci c.g = pun_to_F32(p->bit_and(mask, pun_to_I32(c.g))); 819cb93a386Sopenharmony_ci c.b = pun_to_F32(p->bit_and(mask, pun_to_I32(c.b))); 820cb93a386Sopenharmony_ci c.a = pun_to_F32(p->bit_and(mask, pun_to_I32(c.a))); 821cb93a386Sopenharmony_ci // Notice that even if input_is_opaque, c.a might now be 0. 822cb93a386Sopenharmony_ci } 823cb93a386Sopenharmony_ci 824cb93a386Sopenharmony_ci return c; 825cb93a386Sopenharmony_ci }; 826cb93a386Sopenharmony_ci 827cb93a386Sopenharmony_ci auto sample_level = [&](const SkPixmap& pm, const SkMatrix& inv, skvm::Coord local) { 828cb93a386Sopenharmony_ci const Uniforms u = setup_uniforms(pm); 829cb93a386Sopenharmony_ci 830cb93a386Sopenharmony_ci if (sampling.useCubic) { 831cb93a386Sopenharmony_ci // All bicubic samples have the same fractional offset (fx,fy) from the center. 832cb93a386Sopenharmony_ci // They're either the 16 corners of a 3x3 grid/ surrounding (x,y) at (0.5,0.5) off-center. 833cb93a386Sopenharmony_ci skvm::F32 fx = fract(local.x + 0.5f), 834cb93a386Sopenharmony_ci fy = fract(local.y + 0.5f); 835cb93a386Sopenharmony_ci skvm::F32 wx[4], 836cb93a386Sopenharmony_ci wy[4]; 837cb93a386Sopenharmony_ci 838cb93a386Sopenharmony_ci SkM44 weights = CubicResamplerMatrix(sampling.cubic.B, sampling.cubic.C); 839cb93a386Sopenharmony_ci 840cb93a386Sopenharmony_ci auto dot = [](const skvm::F32 a[], const skvm::F32 b[]) { 841cb93a386Sopenharmony_ci return a[0]*b[0] + a[1]*b[1] + a[2]*b[2] + a[3]*b[3]; 842cb93a386Sopenharmony_ci }; 843cb93a386Sopenharmony_ci const skvm::F32 tmpx[] = { p->splat(1.0f), fx, fx*fx, fx*fx*fx }; 844cb93a386Sopenharmony_ci const skvm::F32 tmpy[] = { p->splat(1.0f), fy, fy*fy, fy*fy*fy }; 845cb93a386Sopenharmony_ci 846cb93a386Sopenharmony_ci for (int row = 0; row < 4; ++row) { 847cb93a386Sopenharmony_ci SkV4 r = weights.row(row); 848cb93a386Sopenharmony_ci skvm::F32 ru[] = { 849cb93a386Sopenharmony_ci p->uniformF(uniforms->pushF(r[0])), 850cb93a386Sopenharmony_ci p->uniformF(uniforms->pushF(r[1])), 851cb93a386Sopenharmony_ci p->uniformF(uniforms->pushF(r[2])), 852cb93a386Sopenharmony_ci p->uniformF(uniforms->pushF(r[3])), 853cb93a386Sopenharmony_ci }; 854cb93a386Sopenharmony_ci wx[row] = dot(ru, tmpx); 855cb93a386Sopenharmony_ci wy[row] = dot(ru, tmpy); 856cb93a386Sopenharmony_ci } 857cb93a386Sopenharmony_ci 858cb93a386Sopenharmony_ci skvm::Color c; 859cb93a386Sopenharmony_ci c.r = c.g = c.b = c.a = p->splat(0.0f); 860cb93a386Sopenharmony_ci 861cb93a386Sopenharmony_ci skvm::F32 sy = local.y - 1.5f; 862cb93a386Sopenharmony_ci for (int j = 0; j < 4; j++, sy += 1.0f) { 863cb93a386Sopenharmony_ci skvm::F32 sx = local.x - 1.5f; 864cb93a386Sopenharmony_ci for (int i = 0; i < 4; i++, sx += 1.0f) { 865cb93a386Sopenharmony_ci skvm::Color s = sample_texel(u, sx,sy); 866cb93a386Sopenharmony_ci skvm::F32 w = wx[i] * wy[j]; 867cb93a386Sopenharmony_ci 868cb93a386Sopenharmony_ci c.r += s.r * w; 869cb93a386Sopenharmony_ci c.g += s.g * w; 870cb93a386Sopenharmony_ci c.b += s.b * w; 871cb93a386Sopenharmony_ci c.a += s.a * w; 872cb93a386Sopenharmony_ci } 873cb93a386Sopenharmony_ci } 874cb93a386Sopenharmony_ci return c; 875cb93a386Sopenharmony_ci } else if (sampling.filter == SkFilterMode::kLinear) { 876cb93a386Sopenharmony_ci // Our four sample points are the corners of a logical 1x1 pixel 877cb93a386Sopenharmony_ci // box surrounding (x,y) at (0.5,0.5) off-center. 878cb93a386Sopenharmony_ci skvm::F32 left = local.x - 0.5f, 879cb93a386Sopenharmony_ci top = local.y - 0.5f, 880cb93a386Sopenharmony_ci right = local.x + 0.5f, 881cb93a386Sopenharmony_ci bottom = local.y + 0.5f; 882cb93a386Sopenharmony_ci 883cb93a386Sopenharmony_ci // The fractional parts of right and bottom are our lerp factors in x and y respectively. 884cb93a386Sopenharmony_ci skvm::F32 fx = fract(right ), 885cb93a386Sopenharmony_ci fy = fract(bottom); 886cb93a386Sopenharmony_ci 887cb93a386Sopenharmony_ci return lerp(lerp(sample_texel(u, left,top ), sample_texel(u, right,top ), fx), 888cb93a386Sopenharmony_ci lerp(sample_texel(u, left,bottom), sample_texel(u, right,bottom), fx), fy); 889cb93a386Sopenharmony_ci } else { 890cb93a386Sopenharmony_ci SkASSERT(sampling.filter == SkFilterMode::kNearest); 891cb93a386Sopenharmony_ci return sample_texel(u, local.x,local.y); 892cb93a386Sopenharmony_ci } 893cb93a386Sopenharmony_ci }; 894cb93a386Sopenharmony_ci 895cb93a386Sopenharmony_ci skvm::Color c = sample_level(upper, upperInv, upperLocal); 896cb93a386Sopenharmony_ci if (lower) { 897cb93a386Sopenharmony_ci auto lowerLocal = SkShaderBase::ApplyMatrix(p, lowerInv, origLocal, uniforms); 898cb93a386Sopenharmony_ci // lower * weight + upper * (1 - weight) 899cb93a386Sopenharmony_ci c = lerp(c, 900cb93a386Sopenharmony_ci sample_level(*lower, lowerInv, lowerLocal), 901cb93a386Sopenharmony_ci p->uniformF(uniforms->pushF(lowerWeight))); 902cb93a386Sopenharmony_ci } 903cb93a386Sopenharmony_ci 904cb93a386Sopenharmony_ci // If the input is opaque and we're not in decal mode, that means the output is too. 905cb93a386Sopenharmony_ci // Forcing *a to 1.0 here will retroactively skip any work we did to interpolate sample alphas. 906cb93a386Sopenharmony_ci if (input_is_opaque 907cb93a386Sopenharmony_ci && fTileModeX != SkTileMode::kDecal 908cb93a386Sopenharmony_ci && fTileModeY != SkTileMode::kDecal) { 909cb93a386Sopenharmony_ci c.a = p->splat(1.0f); 910cb93a386Sopenharmony_ci } 911cb93a386Sopenharmony_ci 912cb93a386Sopenharmony_ci // Alpha-only images get their color from the paint (already converted to dst color space). 913cb93a386Sopenharmony_ci SkColorSpace* cs = upper.colorSpace(); 914cb93a386Sopenharmony_ci SkAlphaType at = upper.alphaType(); 915cb93a386Sopenharmony_ci if (SkColorTypeIsAlphaOnly(upper.colorType())) { 916cb93a386Sopenharmony_ci c.r = paint.r; 917cb93a386Sopenharmony_ci c.g = paint.g; 918cb93a386Sopenharmony_ci c.b = paint.b; 919cb93a386Sopenharmony_ci 920cb93a386Sopenharmony_ci cs = dst.colorSpace(); 921cb93a386Sopenharmony_ci at = kUnpremul_SkAlphaType; 922cb93a386Sopenharmony_ci } 923cb93a386Sopenharmony_ci 924cb93a386Sopenharmony_ci if (sampling.useCubic) { 925cb93a386Sopenharmony_ci // Bicubic filtering naturally produces out of range values on both sides of [0,1]. 926cb93a386Sopenharmony_ci c.a = clamp01(c.a); 927cb93a386Sopenharmony_ci 928cb93a386Sopenharmony_ci skvm::F32 limit = (at == kUnpremul_SkAlphaType || fClampAsIfUnpremul) 929cb93a386Sopenharmony_ci ? p->splat(1.0f) 930cb93a386Sopenharmony_ci : c.a; 931cb93a386Sopenharmony_ci c.r = clamp(c.r, 0.0f, limit); 932cb93a386Sopenharmony_ci c.g = clamp(c.g, 0.0f, limit); 933cb93a386Sopenharmony_ci c.b = clamp(c.b, 0.0f, limit); 934cb93a386Sopenharmony_ci } 935cb93a386Sopenharmony_ci 936cb93a386Sopenharmony_ci return SkColorSpaceXformSteps{cs,at, dst.colorSpace(),dst.alphaType()}.program(p, uniforms, c); 937cb93a386Sopenharmony_ci} 938