1 /*
2  * Copyright 2015 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "src/shaders/SkImageShader.h"
9 
10 #include "src/core/SkArenaAlloc.h"
11 #include "src/core/SkBitmapController.h"
12 #include "src/core/SkColorSpacePriv.h"
13 #include "src/core/SkColorSpaceXformSteps.h"
14 #include "src/core/SkMatrixProvider.h"
15 #include "src/core/SkOpts.h"
16 #include "src/core/SkRasterPipeline.h"
17 #include "src/core/SkReadBuffer.h"
18 #include "src/core/SkScopeExit.h"
19 #include "src/core/SkVM.h"
20 #include "src/core/SkWriteBuffer.h"
21 #include "src/image/SkImage_Base.h"
22 #include "src/shaders/SkBitmapProcShader.h"
23 #include "src/shaders/SkEmptyShader.h"
24 
CubicResamplerMatrix(float B,float C)25 SkM44 SkImageShader::CubicResamplerMatrix(float B, float C) {
26 #if 0
27     constexpr SkM44 kMitchell = SkM44( 1.f/18.f, -9.f/18.f,  15.f/18.f,  -7.f/18.f,
28                                       16.f/18.f,  0.f/18.f, -36.f/18.f,  21.f/18.f,
29                                        1.f/18.f,  9.f/18.f,  27.f/18.f, -21.f/18.f,
30                                        0.f/18.f,  0.f/18.f,  -6.f/18.f,   7.f/18.f);
31 
32     constexpr SkM44 kCatmull = SkM44(0.0f, -0.5f,  1.0f, -0.5f,
33                                      1.0f,  0.0f, -2.5f,  1.5f,
34                                      0.0f,  0.5f,  2.0f, -1.5f,
35                                      0.0f,  0.0f, -0.5f,  0.5f);
36 
37     if (B == 1.0f/3 && C == 1.0f/3) {
38         return kMitchell;
39     }
40     if (B == 0 && C == 0.5f) {
41         return kCatmull;
42     }
43 #endif
44     return SkM44(    (1.f/6)*B, -(3.f/6)*B - C,       (3.f/6)*B + 2*C,    - (1.f/6)*B - C,
45                  1 - (2.f/6)*B,              0, -3 + (12.f/6)*B +   C,  2 - (9.f/6)*B - C,
46                      (1.f/6)*B,  (3.f/6)*B + C,  3 - (15.f/6)*B - 2*C, -2 + (9.f/6)*B + C,
47                              0,              0,                    -C,      (1.f/6)*B + C);
48 }
49 
50 /**
51  *  We are faster in clamp, so always use that tiling when we can.
52  */
optimize(SkTileMode tm,int dimension)53 static SkTileMode optimize(SkTileMode tm, int dimension) {
54     SkASSERT(dimension > 0);
55 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
56     // need to update frameworks/base/libs/hwui/tests/unit/SkiaBehaviorTests.cpp:55 to allow
57     // for transforming to clamp.
58     return tm;
59 #else
60     return dimension == 1 ? SkTileMode::kClamp : tm;
61 #endif
62 }
63 
SkImageShader(sk_sp<SkImage> img,SkTileMode tmx,SkTileMode tmy,const SkMatrix * localMatrix,FilterEnum filtering,bool clampAsIfUnpremul)64 SkImageShader::SkImageShader(sk_sp<SkImage> img,
65                              SkTileMode tmx, SkTileMode tmy,
66                              const SkMatrix* localMatrix,
67                              FilterEnum filtering,
68                              bool clampAsIfUnpremul)
69     : INHERITED(localMatrix)
70     , fImage(std::move(img))
71     , fTileModeX(optimize(tmx, fImage->width()))
72     , fTileModeY(optimize(tmy, fImage->height()))
73     , fFilterEnum(filtering)
74     , fClampAsIfUnpremul(clampAsIfUnpremul)
75     , fFilterOptions({})    // ignored
76 {
77     SkASSERT(filtering != kUseFilterOptions);
78 }
79 
SkImageShader(sk_sp<SkImage> img,SkTileMode tmx,SkTileMode tmy,const SkSamplingOptions & options,const SkMatrix * localMatrix)80 SkImageShader::SkImageShader(sk_sp<SkImage> img,
81                              SkTileMode tmx, SkTileMode tmy,
82                              const SkSamplingOptions& options,
83                              const SkMatrix* localMatrix)
84     : INHERITED(localMatrix)
85     , fImage(std::move(img))
86     , fTileModeX(optimize(tmx, fImage->width()))
87     , fTileModeY(optimize(tmy, fImage->height()))
88     , fFilterEnum(options.fUseCubic ? FilterEnum::kUseCubicResampler
89                                     : FilterEnum::kUseFilterOptions)
90     , fClampAsIfUnpremul(false)
91     , fFilterOptions(options.fFilter)
92     , fCubic(options.fCubic)
93 {}
94 
95 // fClampAsIfUnpremul is always false when constructed through public APIs,
96 // so there's no need to read or write it here.
97 
CreateProc(SkReadBuffer & buffer)98 sk_sp<SkFlattenable> SkImageShader::CreateProc(SkReadBuffer& buffer) {
99     auto tmx = buffer.read32LE<SkTileMode>(SkTileMode::kLastTileMode);
100     auto tmy = buffer.read32LE<SkTileMode>(SkTileMode::kLastTileMode);
101 
102     FilterEnum fe = kInheritFromPaint;
103     if (!buffer.isVersionLT(SkPicturePriv::kFilterEnumInImageShader_Version)) {
104         fe = buffer.read32LE<FilterEnum>(kLast);
105     }
106 
107     SkSamplingOptions op;
108 
109     if (buffer.isVersionLT(SkPicturePriv::kCubicResamplerImageShader_Version)) {
110         if (!buffer.isVersionLT(SkPicturePriv::kFilterOptionsInImageShader_Version)) {
111             op.fUseCubic = false;
112             op.fFilter.fSampling = buffer.read32LE<SkSamplingMode>(SkSamplingMode::kLinear);
113             op.fFilter.fMipmap   = buffer.read32LE<SkMipmapMode>(SkMipmapMode::kLinear);
114         }
115     } else {
116         switch (fe) {
117             case kUseFilterOptions:
118                 op.fUseCubic = false;
119                 op.fFilter.fSampling = buffer.read32LE<SkSamplingMode>(SkSamplingMode::kLinear);
120                 op.fFilter.fMipmap   = buffer.read32LE<SkMipmapMode>(SkMipmapMode::kLinear);
121                 break;
122             case kUseCubicResampler:
123                 op.fUseCubic = true;
124                 op.fCubic.B = buffer.readScalar();
125                 op.fCubic.C = buffer.readScalar();
126                 break;
127             default:
128                 break;
129         }
130     }
131 
132     SkMatrix localMatrix;
133     buffer.readMatrix(&localMatrix);
134     sk_sp<SkImage> img = buffer.readImage();
135     if (!img) {
136         return nullptr;
137     }
138 
139     switch (fe) {
140         case kUseFilterOptions:
141         case kUseCubicResampler:
142             return SkImageShader::Make(std::move(img), tmx, tmy, op, &localMatrix);
143         default:
144             break;
145     }
146     return SkImageShader::Make(std::move(img), tmx, tmy, &localMatrix, fe);
147 }
148 
flatten(SkWriteBuffer & buffer) const149 void SkImageShader::flatten(SkWriteBuffer& buffer) const {
150     buffer.writeUInt((unsigned)fTileModeX);
151     buffer.writeUInt((unsigned)fTileModeY);
152     buffer.writeUInt((unsigned)fFilterEnum);
153     switch (fFilterEnum) {
154         case kUseCubicResampler:
155             buffer.writeScalar(fCubic.B);
156             buffer.writeScalar(fCubic.C);
157             break;
158         case kUseFilterOptions:
159             buffer.writeUInt((unsigned)fFilterOptions.fSampling);
160             buffer.writeUInt((unsigned)fFilterOptions.fMipmap);
161             break;
162         default:
163             break;
164     }
165     buffer.writeMatrix(this->getLocalMatrix());
166     buffer.writeImage(fImage.get());
167     SkASSERT(fClampAsIfUnpremul == false);
168 }
169 
isOpaque() const170 bool SkImageShader::isOpaque() const {
171     return fImage->isOpaque() &&
172            fTileModeX != SkTileMode::kDecal && fTileModeY != SkTileMode::kDecal;
173 }
174 
175 #ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
legacy_shader_can_handle(const SkMatrix & inv)176 static bool legacy_shader_can_handle(const SkMatrix& inv) {
177     SkASSERT(!inv.hasPerspective());
178 
179     // Scale+translate methods are always present, but affine might not be.
180     if (!SkOpts::S32_alpha_D32_filter_DXDY && !inv.isScaleTranslate()) {
181         return false;
182     }
183 
184     // legacy code uses SkFixed 32.32, so ensure the inverse doesn't map device coordinates
185     // out of range.
186     const SkScalar max_dev_coord = 32767.0f;
187     const SkRect src = inv.mapRect(SkRect::MakeWH(max_dev_coord, max_dev_coord));
188 
189     // take 1/4 of max signed 32bits so we have room to subtract local values
190     const SkScalar max_fixed32dot32 = float(SK_MaxS32) * 0.25f;
191     if (!SkRect::MakeLTRB(-max_fixed32dot32, -max_fixed32dot32,
192                           +max_fixed32dot32, +max_fixed32dot32).contains(src)) {
193         return false;
194     }
195 
196     // legacy shader impl should be able to handle these matrices
197     return true;
198 }
199 
onMakeContext(const ContextRec & rec,SkArenaAlloc * alloc) const200 SkShaderBase::Context* SkImageShader::onMakeContext(const ContextRec& rec,
201                                                     SkArenaAlloc* alloc) const {
202     // we only support the old SkFilterQuality setting
203     if (fFilterEnum > kInheritFromPaint) {
204         return nullptr;
205     }
206 
207     auto quality = this->resolveFiltering(rec.fPaint->getFilterQuality());
208 
209     if (quality == kHigh_SkFilterQuality) {
210         return nullptr;
211     }
212     if (fImage->alphaType() == kUnpremul_SkAlphaType) {
213         return nullptr;
214     }
215     if (fImage->colorType() != kN32_SkColorType) {
216         return nullptr;
217     }
218     if (fTileModeX != fTileModeY) {
219         return nullptr;
220     }
221     if (fTileModeX == SkTileMode::kDecal || fTileModeY == SkTileMode::kDecal) {
222         return nullptr;
223     }
224 
225     // SkBitmapProcShader stores bitmap coordinates in a 16bit buffer,
226     // so it can't handle bitmaps larger than 65535.
227     //
228     // We back off another bit to 32767 to make small amounts of
229     // intermediate math safe, e.g. in
230     //
231     //     SkFixed fx = ...;
232     //     fx = tile(fx + SK_Fixed1);
233     //
234     // we want to make sure (fx + SK_Fixed1) never overflows.
235     if (fImage-> width() > 32767 ||
236         fImage->height() > 32767) {
237         return nullptr;
238     }
239 
240     SkMatrix inv;
241     if (!this->computeTotalInverse(*rec.fMatrix, rec.fLocalMatrix, &inv) ||
242         !legacy_shader_can_handle(inv)) {
243         return nullptr;
244     }
245 
246     if (!rec.isLegacyCompatible(fImage->colorSpace())) {
247         return nullptr;
248     }
249 
250     // Send in a modified paint with different filter-quality if we don't agree with the paint
251     SkPaint modifiedPaint;
252     ContextRec modifiedRec = rec;
253     if (quality != rec.fPaint->getFilterQuality()) {
254         modifiedPaint = *rec.fPaint;
255         modifiedPaint.setFilterQuality(quality);
256         modifiedRec.fPaint = &modifiedPaint;
257     }
258     return SkBitmapProcLegacyShader::MakeContext(*this, fTileModeX, fTileModeY,
259                                                  as_IB(fImage.get()), modifiedRec, alloc);
260 }
261 #endif
262 
onIsAImage(SkMatrix * texM,SkTileMode xy[]) const263 SkImage* SkImageShader::onIsAImage(SkMatrix* texM, SkTileMode xy[]) const {
264     if (texM) {
265         *texM = this->getLocalMatrix();
266     }
267     if (xy) {
268         xy[0] = fTileModeX;
269         xy[1] = fTileModeY;
270     }
271     return const_cast<SkImage*>(fImage.get());
272 }
273 
Make(sk_sp<SkImage> image,SkTileMode tmx,SkTileMode tmy,const SkMatrix * localMatrix,FilterEnum filtering,bool clampAsIfUnpremul)274 sk_sp<SkShader> SkImageShader::Make(sk_sp<SkImage> image,
275                                     SkTileMode tmx, SkTileMode tmy,
276                                     const SkMatrix* localMatrix,
277                                     FilterEnum filtering,
278                                     bool clampAsIfUnpremul) {
279     if (!image) {
280         return sk_make_sp<SkEmptyShader>();
281     }
282     return sk_sp<SkShader>{
283         new SkImageShader(image, tmx, tmy, localMatrix, filtering, clampAsIfUnpremul)
284     };
285 }
286 
Make(sk_sp<SkImage> image,SkTileMode tmx,SkTileMode tmy,const SkSamplingOptions & options,const SkMatrix * localMatrix)287 sk_sp<SkShader> SkImageShader::Make(sk_sp<SkImage> image,
288                                     SkTileMode tmx, SkTileMode tmy,
289                                     const SkSamplingOptions& options,
290                                     const SkMatrix* localMatrix) {
291     auto is_unit = [](float x) {
292         return x >= 0 && x <= 1;
293     };
294     if (options.fUseCubic) {
295         if (!is_unit(options.fCubic.B) || !is_unit(options.fCubic.C)) {
296             return nullptr;
297         }
298     }
299     if (!image) {
300         return sk_make_sp<SkEmptyShader>();
301     }
302     return sk_sp<SkShader>{
303         new SkImageShader(image, tmx, tmy, options, localMatrix)
304     };
305 }
306 
307 ///////////////////////////////////////////////////////////////////////////////////////////////////
308 
309 #if SK_SUPPORT_GPU
310 
311 #include "include/gpu/GrRecordingContext.h"
312 #include "src/gpu/GrBitmapTextureMaker.h"
313 #include "src/gpu/GrCaps.h"
314 #include "src/gpu/GrColorInfo.h"
315 #include "src/gpu/GrImageTextureMaker.h"
316 #include "src/gpu/GrRecordingContextPriv.h"
317 #include "src/gpu/GrTextureAdjuster.h"
318 #include "src/gpu/SkGr.h"
319 #include "src/gpu/effects/GrBicubicEffect.h"
320 #include "src/gpu/effects/GrBlendFragmentProcessor.h"
321 #include "src/gpu/effects/GrTextureEffect.h"
322 
asFragmentProcessor(const GrFPArgs & args) const323 std::unique_ptr<GrFragmentProcessor> SkImageShader::asFragmentProcessor(
324         const GrFPArgs& args) const {
325     const auto lm = this->totalLocalMatrix(args.fPreLocalMatrix);
326     SkMatrix lmInverse;
327     if (!lm->invert(&lmInverse)) {
328         return nullptr;
329     }
330 
331     // This would all be much nicer with std::variant.
332     static constexpr size_t kSize = std::max({sizeof(GrYUVAImageTextureMaker),
333                                               sizeof(GrTextureAdjuster      ),
334                                               sizeof(GrImageTextureMaker    ),
335                                               sizeof(GrBitmapTextureMaker   )});
336     static constexpr size_t kAlign = std::max({alignof(GrYUVAImageTextureMaker),
337                                                alignof(GrTextureAdjuster      ),
338                                                alignof(GrImageTextureMaker    ),
339                                                alignof(GrBitmapTextureMaker   )});
340     alignas(kAlign) char storage[kSize];
341     GrTextureProducer* producer = nullptr;
342     SkScopeExit destroyProducer([&producer]{ if (producer) { producer->~GrTextureProducer(); } });
343 
344     uint32_t pinnedUniqueID;
345     SkBitmap bm;
346     if (as_IB(fImage)->isYUVA()) {
347         producer = new (&storage) GrYUVAImageTextureMaker(args.fContext, fImage.get());
348     } else if (GrSurfaceProxyView view =
349                        as_IB(fImage)->refPinnedView(args.fContext, &pinnedUniqueID)) {
350         GrColorInfo colorInfo;
351         if (args.fContext->priv().caps()->isFormatSRGB(view.proxy()->backendFormat())) {
352             SkASSERT(fImage->colorType() == kRGBA_8888_SkColorType);
353             colorInfo = GrColorInfo(GrColorType::kRGBA_8888_SRGB, fImage->alphaType(),
354                                     fImage->refColorSpace());
355         } else {
356             colorInfo = fImage->imageInfo().colorInfo();
357         }
358         producer = new (&storage)
359                 GrTextureAdjuster(args.fContext, std::move(view), colorInfo, pinnedUniqueID);
360     } else if (fImage->isLazyGenerated()) {
361         producer = new (&storage)
362                 GrImageTextureMaker(args.fContext, fImage.get(), GrImageTexGenPolicy::kDraw);
363     } else if (as_IB(fImage)->getROPixels(nullptr, &bm)) {
364         producer =
365                 new (&storage) GrBitmapTextureMaker(args.fContext, bm, GrImageTexGenPolicy::kDraw);
366     } else {
367         return nullptr;
368     }
369     GrSamplerState::WrapMode wmX = SkTileModeToWrapMode(fTileModeX),
370                              wmY = SkTileModeToWrapMode(fTileModeY);
371     // Must set wrap and filter on the sampler before requesting a texture. In two places
372     // below we check the matrix scale factors to determine how to interpret the filter
373     // quality setting. This completely ignores the complexity of the drawVertices case
374     // where explicit local coords are provided by the caller.
375     bool sharpen = args.fContext->priv().options().fSharpenMipmappedTextures;
376     GrSamplerState::Filter fm;
377     GrSamplerState::MipmapMode mm;
378     bool bicubic;
379     SkCubicResampler kernel = GrBicubicEffect::gMitchell;
380 
381     switch (fFilterEnum) {
382         case FilterEnum::kUseFilterOptions:
383             bicubic = false;
384             switch (fFilterOptions.fSampling) {
385                 case SkSamplingMode::kNearest: fm = GrSamplerState::Filter::kNearest; break;
386                 case SkSamplingMode::kLinear : fm = GrSamplerState::Filter::kLinear ; break;
387             }
388             switch (fFilterOptions.fMipmap) {
389                 case SkMipmapMode::kNone   : mm = GrSamplerState::MipmapMode::kNone   ; break;
390                 case SkMipmapMode::kNearest: mm = GrSamplerState::MipmapMode::kNearest; break;
391                 case SkMipmapMode::kLinear : mm = GrSamplerState::MipmapMode::kLinear ; break;
392             }
393             break;
394         case FilterEnum::kUseCubicResampler:
395             bicubic = true;
396             kernel = fCubic;
397             fm = GrSamplerState::Filter::kNearest;
398             mm = GrSamplerState::MipmapMode::kNone;
399             break;
400         case FilterEnum::kInheritFromPaint:
401         default: // none, low, medium, high
402             std::tie(fm, mm, bicubic) =
403                     GrInterpretFilterQuality(fImage->dimensions(),
404                                              this->resolveFiltering(args.fFilterQuality),
405                                              args.fMatrixProvider.localToDevice(),
406                                              *lm,
407                                              sharpen,
408                                              args.fAllowFilterQualityReduction);
409             break;
410     }
411     std::unique_ptr<GrFragmentProcessor> fp;
412     if (bicubic) {
413         fp = producer->createBicubicFragmentProcessor(lmInverse, nullptr, nullptr, wmX, wmY, kernel);
414     } else {
415         fp = producer->createFragmentProcessor(lmInverse, nullptr, nullptr, {wmX, wmY, fm, mm});
416     }
417     if (!fp) {
418         return nullptr;
419     }
420     fp = GrColorSpaceXformEffect::Make(std::move(fp), fImage->colorSpace(), producer->alphaType(),
421                                        args.fDstColorInfo->colorSpace(), kPremul_SkAlphaType);
422     fp = GrBlendFragmentProcessor::Make(std::move(fp), nullptr, SkBlendMode::kModulate);
423     bool isAlphaOnly = SkColorTypeIsAlphaOnly(fImage->colorType());
424     if (isAlphaOnly) {
425         return fp;
426     } else if (args.fInputColorIsOpaque) {
427         return GrFragmentProcessor::OverrideInput(std::move(fp), SK_PMColor4fWHITE, false);
428     }
429     return GrFragmentProcessor::MulChildByInputAlpha(std::move(fp));
430 }
431 
432 #endif
433 
434 ///////////////////////////////////////////////////////////////////////////////////////////////////
435 #include "src/core/SkImagePriv.h"
436 
SkMakeBitmapShader(const SkBitmap & src,SkTileMode tmx,SkTileMode tmy,const SkMatrix * localMatrix,SkCopyPixelsMode cpm)437 sk_sp<SkShader> SkMakeBitmapShader(const SkBitmap& src, SkTileMode tmx, SkTileMode tmy,
438                                    const SkMatrix* localMatrix, SkCopyPixelsMode cpm) {
439     return SkImageShader::Make(SkMakeImageFromRasterBitmap(src, cpm),
440                                tmx, tmy, localMatrix, SkImageShader::kInheritFromPaint);
441 }
442 
SkMakeBitmapShaderForPaint(const SkPaint & paint,const SkBitmap & src,SkTileMode tmx,SkTileMode tmy,const SkMatrix * localMatrix,SkCopyPixelsMode mode)443 sk_sp<SkShader> SkMakeBitmapShaderForPaint(const SkPaint& paint, const SkBitmap& src,
444                                            SkTileMode tmx, SkTileMode tmy,
445                                            const SkMatrix* localMatrix, SkCopyPixelsMode mode) {
446     auto s = SkMakeBitmapShader(src, tmx, tmy, localMatrix, mode);
447     if (!s) {
448         return nullptr;
449     }
450     if (src.colorType() == kAlpha_8_SkColorType && paint.getShader()) {
451         // Compose the image shader with the paint's shader. Alpha images+shaders should output the
452         // texture's alpha multiplied by the shader's color. DstIn (d*sa) will achieve this with
453         // the source image and dst shader (MakeBlend takes dst first, src second).
454         s = SkShaders::Blend(SkBlendMode::kDstIn, paint.refShader(), std::move(s));
455     }
456     return s;
457 }
458 
RegisterFlattenables()459 void SkShaderBase::RegisterFlattenables() { SK_REGISTER_FLATTENABLE(SkImageShader); }
460 
461 class SkImageStageUpdater : public SkStageUpdater {
462 public:
SkImageStageUpdater(const SkImageShader * shader,bool usePersp)463     SkImageStageUpdater(const SkImageShader* shader, bool usePersp)
464         : fShader(shader)
465         , fUsePersp(usePersp || as_SB(shader)->getLocalMatrix().hasPerspective())
466     {}
467 
468     const SkImageShader* fShader;
469     const bool           fUsePersp; // else use affine
470 
471     // large enough for perspective, though often we just use 2x3
472     float fMatrixStorage[9];
473 
474 #if 0   // TODO: when we support mipmaps
475     SkRasterPipeline_GatherCtx* fGather;
476     SkRasterPipeline_TileCtx* fLimitX;
477     SkRasterPipeline_TileCtx* fLimitY;
478     SkRasterPipeline_DecalTileCtx* fDecal;
479 #endif
480 
append_matrix_stage(SkRasterPipeline * p)481     void append_matrix_stage(SkRasterPipeline* p) {
482         if (fUsePersp) {
483             p->append(SkRasterPipeline::matrix_perspective, fMatrixStorage);
484         } else {
485             p->append(SkRasterPipeline::matrix_2x3, fMatrixStorage);
486         }
487     }
488 
update(const SkMatrix & ctm,const SkMatrix * localM)489     bool update(const SkMatrix& ctm, const SkMatrix* localM) override {
490         SkMatrix matrix;
491         if (fShader->computeTotalInverse(ctm, localM, &matrix)) {
492             if (fUsePersp) {
493                 matrix.get9(fMatrixStorage);
494             } else {
495                 // if we get here, matrix should be affine. If it isn't, then defensively we
496                 // won't draw (by returning false), but we should work to never let this
497                 // happen (i.e. better preflight by the caller to know ahead of time that we
498                 // may encounter perspective, either in the CTM, or in the localM).
499                 //
500                 // See https://bugs.chromium.org/p/skia/issues/detail?id=10004
501                 //
502                 if (!matrix.asAffine(fMatrixStorage)) {
503                     SkASSERT(false);
504                     return false;
505                 }
506             }
507             return true;
508         }
509         return false;
510     }
511 };
512 
tweak_quality_and_inv_matrix(SkFilterQuality * quality,SkMatrix * matrix)513 static void tweak_quality_and_inv_matrix(SkFilterQuality* quality, SkMatrix* matrix) {
514     // When the matrix is just an integer translate, bilerp == nearest neighbor.
515     if (*quality == kLow_SkFilterQuality &&
516             matrix->getType() <= SkMatrix::kTranslate_Mask &&
517             matrix->getTranslateX() == (int)matrix->getTranslateX() &&
518             matrix->getTranslateY() == (int)matrix->getTranslateY()) {
519         *quality = kNone_SkFilterQuality;
520     }
521 
522     // See skia:4649 and the GM image_scale_aligned.
523     if (*quality == kNone_SkFilterQuality) {
524         if (matrix->getScaleX() >= 0) {
525             matrix->setTranslateX(nextafterf(matrix->getTranslateX(),
526                                              floorf(matrix->getTranslateX())));
527         }
528         if (matrix->getScaleY() >= 0) {
529             matrix->setTranslateY(nextafterf(matrix->getTranslateY(),
530                                              floorf(matrix->getTranslateY())));
531         }
532     }
533 }
534 
doStages(const SkStageRec & rec,SkImageStageUpdater * updater) const535 bool SkImageShader::doStages(const SkStageRec& rec, SkImageStageUpdater* updater) const {
536     SkFilterQuality quality;
537     switch (fFilterEnum) {
538         case FilterEnum::kUseFilterOptions:
539         case FilterEnum::kUseCubicResampler:
540             return false;   // TODO: support these in stages
541         case FilterEnum::kInheritFromPaint:
542             quality = rec.fPaint.getFilterQuality();
543             break;
544         default:
545             quality = (SkFilterQuality)fFilterEnum;
546             break;
547     }
548 
549     if (updater && quality == kMedium_SkFilterQuality) {
550         // TODO: medium: recall RequestBitmap and update width/height accordingly
551         return false;
552     }
553 
554     SkRasterPipeline* p = rec.fPipeline;
555     SkArenaAlloc* alloc = rec.fAlloc;
556 
557     SkMatrix matrix;
558     if (!this->computeTotalInverse(rec.fMatrixProvider.localToDevice(), rec.fLocalM, &matrix)) {
559         return false;
560     }
561 
562     const auto* state = SkBitmapController::RequestBitmap(as_IB(fImage.get()),
563                                                           matrix, quality, alloc);
564     if (!state) {
565         return false;
566     }
567 
568     const SkPixmap& pm = state->pixmap();
569     matrix  = state->invMatrix();
570     quality = state->quality();
571     auto info = pm.info();
572 
573     p->append(SkRasterPipeline::seed_shader);
574 
575     if (updater) {
576         updater->append_matrix_stage(p);
577     } else {
578         tweak_quality_and_inv_matrix(&quality, &matrix);
579         p->append_matrix(alloc, matrix);
580     }
581 
582     auto gather = alloc->make<SkRasterPipeline_GatherCtx>();
583     gather->pixels = pm.addr();
584     gather->stride = pm.rowBytesAsPixels();
585     gather->width  = pm.width();
586     gather->height = pm.height();
587 
588     auto limit_x = alloc->make<SkRasterPipeline_TileCtx>(),
589          limit_y = alloc->make<SkRasterPipeline_TileCtx>();
590     limit_x->scale = pm.width();
591     limit_x->invScale = 1.0f / pm.width();
592     limit_y->scale = pm.height();
593     limit_y->invScale = 1.0f / pm.height();
594 
595     SkRasterPipeline_DecalTileCtx* decal_ctx = nullptr;
596     bool decal_x_and_y = fTileModeX == SkTileMode::kDecal && fTileModeY == SkTileMode::kDecal;
597     if (fTileModeX == SkTileMode::kDecal || fTileModeY == SkTileMode::kDecal) {
598         decal_ctx = alloc->make<SkRasterPipeline_DecalTileCtx>();
599         decal_ctx->limit_x = limit_x->scale;
600         decal_ctx->limit_y = limit_y->scale;
601     }
602 
603 #if 0   // TODO: when we support kMedium
604     if (updator && (quality == kMedium_SkFilterQuality)) {
605         // if we change levels in mipmap, we need to update the scales (and invScales)
606         updator->fGather = gather;
607         updator->fLimitX = limit_x;
608         updator->fLimitY = limit_y;
609         updator->fDecal = decal_ctx;
610     }
611 #endif
612 
613     auto append_tiling_and_gather = [&] {
614         if (decal_x_and_y) {
615             p->append(SkRasterPipeline::decal_x_and_y,  decal_ctx);
616         } else {
617             switch (fTileModeX) {
618                 case SkTileMode::kClamp:  /* The gather_xxx stage will clamp for us. */     break;
619                 case SkTileMode::kMirror: p->append(SkRasterPipeline::mirror_x, limit_x);   break;
620                 case SkTileMode::kRepeat: p->append(SkRasterPipeline::repeat_x, limit_x);   break;
621                 case SkTileMode::kDecal:  p->append(SkRasterPipeline::decal_x,  decal_ctx); break;
622             }
623             switch (fTileModeY) {
624                 case SkTileMode::kClamp:  /* The gather_xxx stage will clamp for us. */     break;
625                 case SkTileMode::kMirror: p->append(SkRasterPipeline::mirror_y, limit_y);   break;
626                 case SkTileMode::kRepeat: p->append(SkRasterPipeline::repeat_y, limit_y);   break;
627                 case SkTileMode::kDecal:  p->append(SkRasterPipeline::decal_y,  decal_ctx); break;
628             }
629         }
630 
631         void* ctx = gather;
632         switch (info.colorType()) {
633             case kAlpha_8_SkColorType:      p->append(SkRasterPipeline::gather_a8,      ctx); break;
634             case kA16_unorm_SkColorType:    p->append(SkRasterPipeline::gather_a16,     ctx); break;
635             case kA16_float_SkColorType:    p->append(SkRasterPipeline::gather_af16,    ctx); break;
636             case kRGB_565_SkColorType:      p->append(SkRasterPipeline::gather_565,     ctx); break;
637             case kARGB_4444_SkColorType:    p->append(SkRasterPipeline::gather_4444,    ctx); break;
638             case kR8G8_unorm_SkColorType:   p->append(SkRasterPipeline::gather_rg88,    ctx); break;
639             case kR16G16_unorm_SkColorType: p->append(SkRasterPipeline::gather_rg1616,  ctx); break;
640             case kR16G16_float_SkColorType: p->append(SkRasterPipeline::gather_rgf16,  ctx);  break;
641             case kRGBA_8888_SkColorType:    p->append(SkRasterPipeline::gather_8888,    ctx); break;
642             case kRGBA_1010102_SkColorType: p->append(SkRasterPipeline::gather_1010102, ctx); break;
643             case kR16G16B16A16_unorm_SkColorType:
644                                             p->append(SkRasterPipeline::gather_16161616,ctx); break;
645             case kRGBA_F16Norm_SkColorType:
646             case kRGBA_F16_SkColorType:     p->append(SkRasterPipeline::gather_f16,     ctx); break;
647             case kRGBA_F32_SkColorType:     p->append(SkRasterPipeline::gather_f32,     ctx); break;
648 
649             case kGray_8_SkColorType:       p->append(SkRasterPipeline::gather_a8,      ctx);
650                                             p->append(SkRasterPipeline::alpha_to_gray      ); break;
651 
652             case kRGB_888x_SkColorType:     p->append(SkRasterPipeline::gather_8888,    ctx);
653                                             p->append(SkRasterPipeline::force_opaque       ); break;
654 
655             case kBGRA_1010102_SkColorType: p->append(SkRasterPipeline::gather_1010102, ctx);
656                                             p->append(SkRasterPipeline::swap_rb            ); break;
657 
658             case kRGB_101010x_SkColorType:  p->append(SkRasterPipeline::gather_1010102, ctx);
659                                             p->append(SkRasterPipeline::force_opaque       ); break;
660 
661             case kBGR_101010x_SkColorType:  p->append(SkRasterPipeline::gather_1010102, ctx);
662                                             p->append(SkRasterPipeline::force_opaque       );
663                                             p->append(SkRasterPipeline::swap_rb            ); break;
664 
665             case kBGRA_8888_SkColorType:    p->append(SkRasterPipeline::gather_8888,    ctx);
666                                             p->append(SkRasterPipeline::swap_rb            ); break;
667 
668             case kUnknown_SkColorType: SkASSERT(false);
669         }
670         if (decal_ctx) {
671             p->append(SkRasterPipeline::check_decal_mask, decal_ctx);
672         }
673     };
674 
675     auto append_misc = [&] {
676         SkColorSpace* cs = info.colorSpace();
677         SkAlphaType   at = info.alphaType();
678 
679         // Color for A8 images comes from the paint.  TODO: all alpha images?  none?
680         if (info.colorType() == kAlpha_8_SkColorType) {
681             SkColor4f rgb = rec.fPaint.getColor4f();
682             p->append_set_rgb(alloc, rgb);
683 
684             cs = sk_srgb_singleton();
685             at = kUnpremul_SkAlphaType;
686         }
687 
688         // Bicubic filtering naturally produces out of range values on both sides of [0,1].
689         if (quality == kHigh_SkFilterQuality) {
690             p->append(SkRasterPipeline::clamp_0);
691             p->append(at == kUnpremul_SkAlphaType || fClampAsIfUnpremul
692                           ? SkRasterPipeline::clamp_1
693                           : SkRasterPipeline::clamp_a);
694         }
695 
696         // Transform color space and alpha type to match shader convention (dst CS, premul alpha).
697         alloc->make<SkColorSpaceXformSteps>(cs, at,
698                                             rec.fDstCS, kPremul_SkAlphaType)
699             ->apply(p);
700 
701         return true;
702     };
703 
704     // Check for fast-path stages.
705     auto ct = info.colorType();
706     if (true
707         && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType)
708         && quality == kLow_SkFilterQuality
709         && fTileModeX == SkTileMode::kClamp && fTileModeY == SkTileMode::kClamp) {
710 
711         p->append(SkRasterPipeline::bilerp_clamp_8888, gather);
712         if (ct == kBGRA_8888_SkColorType) {
713             p->append(SkRasterPipeline::swap_rb);
714         }
715         return append_misc();
716     }
717     if (true
718         && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType) // TODO: all formats
719         && quality == kLow_SkFilterQuality
720         && fTileModeX != SkTileMode::kDecal // TODO decal too?
721         && fTileModeY != SkTileMode::kDecal) {
722 
723         auto ctx = alloc->make<SkRasterPipeline_SamplerCtx2>();
724         *(SkRasterPipeline_GatherCtx*)(ctx) = *gather;
725         ctx->ct = ct;
726         ctx->tileX = fTileModeX;
727         ctx->tileY = fTileModeY;
728         ctx->invWidth  = 1.0f / ctx->width;
729         ctx->invHeight = 1.0f / ctx->height;
730         p->append(SkRasterPipeline::bilinear, ctx);
731         return append_misc();
732     }
733     if (true
734         && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType)
735         && quality == kHigh_SkFilterQuality
736         && fTileModeX == SkTileMode::kClamp && fTileModeY == SkTileMode::kClamp) {
737 
738         p->append(SkRasterPipeline::bicubic_clamp_8888, gather);
739         if (ct == kBGRA_8888_SkColorType) {
740             p->append(SkRasterPipeline::swap_rb);
741         }
742         return append_misc();
743     }
744     if (true
745         && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType) // TODO: all formats
746         && quality == kHigh_SkFilterQuality
747         && fTileModeX != SkTileMode::kDecal // TODO decal too?
748         && fTileModeY != SkTileMode::kDecal) {
749 
750         auto ctx = alloc->make<SkRasterPipeline_SamplerCtx2>();
751         *(SkRasterPipeline_GatherCtx*)(ctx) = *gather;
752         ctx->ct = ct;
753         ctx->tileX = fTileModeX;
754         ctx->tileY = fTileModeY;
755         ctx->invWidth  = 1.0f / ctx->width;
756         ctx->invHeight = 1.0f / ctx->height;
757         p->append(SkRasterPipeline::bicubic, ctx);
758         return append_misc();
759     }
760 
761     SkRasterPipeline_SamplerCtx* sampler = nullptr;
762     if (quality != kNone_SkFilterQuality) {
763         sampler = alloc->make<SkRasterPipeline_SamplerCtx>();
764     }
765 
766     auto sample = [&](SkRasterPipeline::StockStage setup_x,
767                       SkRasterPipeline::StockStage setup_y) {
768         p->append(setup_x, sampler);
769         p->append(setup_y, sampler);
770         append_tiling_and_gather();
771         p->append(SkRasterPipeline::accumulate, sampler);
772     };
773 
774     if (quality == kNone_SkFilterQuality) {
775         append_tiling_and_gather();
776     } else if (quality == kLow_SkFilterQuality) {
777         p->append(SkRasterPipeline::save_xy, sampler);
778 
779         sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_ny);
780         sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_ny);
781         sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_py);
782         sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_py);
783 
784         p->append(SkRasterPipeline::move_dst_src);
785 
786     } else {
787         SkASSERT(quality == kHigh_SkFilterQuality);
788         p->append(SkRasterPipeline::save_xy, sampler);
789 
790         sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n3y);
791         sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n3y);
792         sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n3y);
793         sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n3y);
794 
795         sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n1y);
796         sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n1y);
797         sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n1y);
798         sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n1y);
799 
800         sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p1y);
801         sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p1y);
802         sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p1y);
803         sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p1y);
804 
805         sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p3y);
806         sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p3y);
807         sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p3y);
808         sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p3y);
809 
810         p->append(SkRasterPipeline::move_dst_src);
811     }
812 
813     return append_misc();
814 }
815 
onAppendStages(const SkStageRec & rec) const816 bool SkImageShader::onAppendStages(const SkStageRec& rec) const {
817     return this->doStages(rec, nullptr);
818 }
819 
onAppendUpdatableStages(const SkStageRec & rec) const820 SkStageUpdater* SkImageShader::onAppendUpdatableStages(const SkStageRec& rec) const {
821     bool usePersp = rec.fMatrixProvider.localToDevice().hasPerspective();
822     auto updater = rec.fAlloc->make<SkImageStageUpdater>(this, usePersp);
823     return this->doStages(rec, updater) ? updater : nullptr;
824 }
825 
826 enum class SamplingEnum {
827     kNearest,
828     kLinear,
829     kBicubic,
830 };
831 
onProgram(skvm::Builder * p,skvm::Coord device,skvm::Coord origLocal,skvm::Color paint,const SkMatrixProvider & matrices,const SkMatrix * localM,SkFilterQuality paintQuality,const SkColorInfo & dst,skvm::Uniforms * uniforms,SkArenaAlloc * alloc) const832 skvm::Color SkImageShader::onProgram(skvm::Builder* p,
833                                      skvm::Coord device, skvm::Coord origLocal, skvm::Color paint,
834                                      const SkMatrixProvider& matrices, const SkMatrix* localM,
835                                      SkFilterQuality paintQuality, const SkColorInfo& dst,
836                                      skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
837     SkMatrix baseInv;
838     if (!this->computeTotalInverse(matrices.localToDevice(), localM, &baseInv)) {
839         return {};
840     }
841     baseInv.normalizePerspective();
842 
843     const SkPixmap *upper = nullptr,
844                    *lower = nullptr;
845     SkMatrix        upperInv;
846     float           lowerWeight = 0;
847     SamplingEnum    sampling = (SamplingEnum)fFilterOptions.fSampling;
848 
849     auto post_scale = [&](SkISize level, const SkMatrix& base) {
850         return SkMatrix::Scale(SkIntToScalar(level.width())  / fImage->width(),
851                                SkIntToScalar(level.height()) / fImage->height())
852                 * base;
853     };
854 
855     if (fFilterEnum == kUseFilterOptions) {
856         auto* access = alloc->make<SkMipmapAccessor>(as_IB(fImage.get()), baseInv,
857                                                      fFilterOptions.fMipmap);
858         upper = &access->level();
859         upperInv = post_scale(upper->dimensions(), baseInv);
860         lowerWeight = access->lowerWeight();
861         if (lowerWeight > 0) {
862             lower = &access->lowerLevel();
863         }
864     } else if (fFilterEnum == kUseCubicResampler){
865         auto* access = alloc->make<SkMipmapAccessor>(as_IB(fImage.get()), baseInv,
866                                                      SkMipmapMode::kNone);
867         upper = &access->level();
868         upperInv = post_scale(upper->dimensions(), baseInv);
869         sampling = SamplingEnum::kBicubic;
870     } else {
871         // Convert from the filter-quality enum to our working description:
872         //  sampling : nearest, bilerp, bicubic
873         //  miplevel(s) and associated matrices
874         //
875         SkFilterQuality quality = paintQuality;
876         if (fFilterEnum != kInheritFromPaint) {
877             quality = (SkFilterQuality)fFilterEnum;
878         }
879 
880         // We use RequestBitmap() to make sure our SkBitmapController::State lives in the alloc.
881         // This lets the SkVMBlitter hang on to this state and keep our image alive.
882         auto state = SkBitmapController::RequestBitmap(as_IB(fImage.get()), baseInv, quality, alloc);
883         if (!state) {
884             return {};
885         }
886         upper    = &state->pixmap();
887         upperInv = state->invMatrix();
888 
889         quality  = state->quality();
890         tweak_quality_and_inv_matrix(&quality, &upperInv);
891         switch (quality) {
892             case kNone_SkFilterQuality:   sampling = SamplingEnum::kNearest; break;
893             case kLow_SkFilterQuality:    sampling = SamplingEnum::kLinear;  break;
894             case kMedium_SkFilterQuality: sampling = SamplingEnum::kLinear;  break;
895             case kHigh_SkFilterQuality:   sampling = SamplingEnum::kBicubic; break;
896         }
897     }
898 
899     skvm::Coord upperLocal = SkShaderBase::ApplyMatrix(p, upperInv, origLocal, uniforms);
900 
901     // All existing SkColorTypes pass these checks.  We'd only fail here adding new ones.
902     skvm::PixelFormat unused;
903     if (true  && !SkColorType_to_PixelFormat(upper->colorType(), &unused)) {
904         return {};
905     }
906     if (lower && !SkColorType_to_PixelFormat(lower->colorType(), &unused)) {
907         return {};
908     }
909 
910     // We can exploit image opacity to skip work unpacking alpha channels.
911     const bool input_is_opaque = SkAlphaTypeIsOpaque(upper->alphaType())
912                               || SkColorTypeIsAlwaysOpaque(upper->colorType());
913 
914     // Each call to sample() will try to rewrite the same uniforms over and over,
915     // so remember where we start and reset back there each time.  That way each
916     // sample() call uses the same uniform offsets.
917 
918     auto compute_clamp_limit = [&](float limit) {
919         // Subtract an ulp so the upper clamp limit excludes limit itself.
920         int bits;
921         memcpy(&bits, &limit, 4);
922         return p->uniformF(uniforms->push(bits-1));
923     };
924 
925     // Except in the simplest case (no mips, no filtering), we reference uniforms
926     // more than once. To avoid adding/registering them multiple times, we pre-load them
927     // into a struct (just to logically group them together), based on the "current"
928     // pixmap (level of a mipmap).
929     //
930     struct Uniforms {
931         skvm::F32   w, iw, i2w,
932                     h, ih, i2h;
933 
934         skvm::F32   clamp_w,
935                     clamp_h;
936 
937         skvm::Uniform addr;
938         skvm::I32     rowBytesAsPixels;
939 
940         skvm::PixelFormat pixelFormat;  // not a uniform, but needed for each texel sample,
941                                         // so we store it here, since it is also dependent on
942                                         // the current pixmap (level).
943     };
944 
945     auto setup_uniforms = [&](const SkPixmap& pm) -> Uniforms {
946         skvm::PixelFormat pixelFormat;
947         SkAssertResult(SkColorType_to_PixelFormat(pm.colorType(), &pixelFormat));
948         return {
949             p->uniformF(uniforms->pushF(     pm.width())),
950             p->uniformF(uniforms->pushF(1.0f/pm.width())), // iff tileX == kRepeat
951             p->uniformF(uniforms->pushF(0.5f/pm.width())), // iff tileX == kMirror
952 
953             p->uniformF(uniforms->pushF(     pm.height())),
954             p->uniformF(uniforms->pushF(1.0f/pm.height())), // iff tileY == kRepeat
955             p->uniformF(uniforms->pushF(0.5f/pm.height())), // iff tileY == kMirror
956 
957             compute_clamp_limit(pm. width()),
958             compute_clamp_limit(pm.height()),
959 
960             uniforms->pushPtr(pm.addr()),
961             p->uniform32(uniforms->push(pm.rowBytesAsPixels())),
962 
963             pixelFormat,
964         };
965     };
966 
967     auto sample_texel = [&](const Uniforms& u, skvm::F32 sx, skvm::F32 sy) -> skvm::Color {
968         // repeat() and mirror() are written assuming they'll be followed by a [0,scale) clamp.
969         auto repeat = [&](skvm::F32 v, skvm::F32 S, skvm::F32 I) {
970             return v - floor(v * I) * S;
971         };
972         auto mirror = [&](skvm::F32 v, skvm::F32 S, skvm::F32 I2) {
973             // abs( (v-scale) - (2*scale)*floor((v-scale)*(0.5f/scale)) - scale )
974             //      {---A---}   {------------------B------------------}
975             skvm::F32 A = v - S,
976                       B = (S + S) * floor(A * I2);
977             return abs(A - B - S);
978         };
979         switch (fTileModeX) {
980             case SkTileMode::kDecal:  /* handled after gather */ break;
981             case SkTileMode::kClamp:  /*    we always clamp   */ break;
982             case SkTileMode::kRepeat: sx = repeat(sx, u.w, u.iw);  break;
983             case SkTileMode::kMirror: sx = mirror(sx, u.w, u.i2w); break;
984         }
985         switch (fTileModeY) {
986             case SkTileMode::kDecal:  /* handled after gather */  break;
987             case SkTileMode::kClamp:  /*    we always clamp   */  break;
988             case SkTileMode::kRepeat: sy = repeat(sy, u.h, u.ih);  break;
989             case SkTileMode::kMirror: sy = mirror(sy, u.h, u.i2h); break;
990         }
991 
992         // Always clamp sample coordinates to [0,width), [0,height), both for memory
993         // safety and to handle the clamps still needed by kClamp, kRepeat, and kMirror.
994         skvm::F32 clamped_x = clamp(sx, 0, u.clamp_w),
995                   clamped_y = clamp(sy, 0, u.clamp_h);
996 
997         // Load pixels from pm.addr()[(int)sx + (int)sy*stride].
998         skvm::I32 index = trunc(clamped_x) +
999                           trunc(clamped_y) * u.rowBytesAsPixels;
1000         skvm::Color c = gather(u.pixelFormat, u.addr, index);
1001 
1002         // If we know the image is opaque, jump right to alpha = 1.0f, skipping work to unpack it.
1003         if (input_is_opaque) {
1004             c.a = p->splat(1.0f);
1005         }
1006 
1007         // Mask away any pixels that we tried to sample outside the bounds in kDecal.
1008         if (fTileModeX == SkTileMode::kDecal || fTileModeY == SkTileMode::kDecal) {
1009             skvm::I32 mask = p->splat(~0);
1010             if (fTileModeX == SkTileMode::kDecal) { mask &= (sx == clamped_x); }
1011             if (fTileModeY == SkTileMode::kDecal) { mask &= (sy == clamped_y); }
1012             c.r = bit_cast(p->bit_and(mask, bit_cast(c.r)));
1013             c.g = bit_cast(p->bit_and(mask, bit_cast(c.g)));
1014             c.b = bit_cast(p->bit_and(mask, bit_cast(c.b)));
1015             c.a = bit_cast(p->bit_and(mask, bit_cast(c.a)));
1016             // Notice that even if input_is_opaque, c.a might now be 0.
1017         }
1018 
1019         return c;
1020     };
1021 
1022     auto sample_level = [&](const SkPixmap& pm, const SkMatrix& inv, skvm::Coord local) {
1023         const Uniforms u = setup_uniforms(pm);
1024 
1025         if (sampling == SamplingEnum::kNearest) {
1026             return sample_texel(u, local.x,local.y);
1027         } else if (sampling == SamplingEnum::kLinear) {
1028             // Our four sample points are the corners of a logical 1x1 pixel
1029             // box surrounding (x,y) at (0.5,0.5) off-center.
1030             skvm::F32 left   = local.x - 0.5f,
1031                       top    = local.y - 0.5f,
1032                       right  = local.x + 0.5f,
1033                       bottom = local.y + 0.5f;
1034 
1035             // The fractional parts of right and bottom are our lerp factors in x and y respectively.
1036             skvm::F32 fx = fract(right ),
1037                       fy = fract(bottom);
1038 
1039             return lerp(lerp(sample_texel(u, left,top   ), sample_texel(u, right,top   ), fx),
1040                         lerp(sample_texel(u, left,bottom), sample_texel(u, right,bottom), fx), fy);
1041         } else {
1042             SkASSERT(sampling == SamplingEnum::kBicubic);
1043 
1044             // All bicubic samples have the same fractional offset (fx,fy) from the center.
1045             // They're either the 16 corners of a 3x3 grid/ surrounding (x,y) at (0.5,0.5) off-center.
1046             skvm::F32 fx = fract(local.x + 0.5f),
1047                       fy = fract(local.y + 0.5f);
1048             skvm::F32 wx[4],
1049                       wy[4];
1050 
1051             SkM44 weights = CubicResamplerMatrix(fCubic.B, fCubic.C);
1052 
1053             auto dot = [](const skvm::F32 a[], const skvm::F32 b[]) {
1054                 return a[0]*b[0] + a[1]*b[1] + a[2]*b[2] + a[3]*b[3];
1055             };
1056             const skvm::F32 tmpx[] =  { p->splat(1.0f), fx, fx*fx, fx*fx*fx };
1057             const skvm::F32 tmpy[] =  { p->splat(1.0f), fy, fy*fy, fy*fy*fy };
1058 
1059             for (int row = 0; row < 4; ++row) {
1060                 SkV4 r = weights.row(row);
1061                 skvm::F32 ru[] = {
1062                     p->uniformF(uniforms->pushF(r[0])),
1063                     p->uniformF(uniforms->pushF(r[1])),
1064                     p->uniformF(uniforms->pushF(r[2])),
1065                     p->uniformF(uniforms->pushF(r[3])),
1066                 };
1067                 wx[row] = dot(ru, tmpx);
1068                 wy[row] = dot(ru, tmpy);
1069             }
1070 
1071             skvm::Color c;
1072             c.r = c.g = c.b = c.a = p->splat(0.0f);
1073 
1074             skvm::F32 sy = local.y - 1.5f;
1075             for (int j = 0; j < 4; j++, sy += 1.0f) {
1076                 skvm::F32 sx = local.x - 1.5f;
1077                 for (int i = 0; i < 4; i++, sx += 1.0f) {
1078                     skvm::Color s = sample_texel(u, sx,sy);
1079                     skvm::F32   w = wx[i] * wy[j];
1080 
1081                     c.r += s.r * w;
1082                     c.g += s.g * w;
1083                     c.b += s.b * w;
1084                     c.a += s.a * w;
1085                 }
1086             }
1087             return c;
1088         }
1089     };
1090 
1091     skvm::Color c = sample_level(*upper, upperInv, upperLocal);
1092     if (lower) {
1093         auto lowerInv = post_scale(lower->dimensions(), baseInv);
1094         auto lowerLocal = SkShaderBase::ApplyMatrix(p, lowerInv, origLocal, uniforms);
1095         // lower * weight + upper * (1 - weight)
1096         c = lerp(c,
1097                  sample_level(*lower, lowerInv, lowerLocal),
1098                  p->uniformF(uniforms->pushF(lowerWeight)));
1099     }
1100 
1101     // If the input is opaque and we're not in decal mode, that means the output is too.
1102     // Forcing *a to 1.0 here will retroactively skip any work we did to interpolate sample alphas.
1103     if (input_is_opaque
1104             && fTileModeX != SkTileMode::kDecal
1105             && fTileModeY != SkTileMode::kDecal) {
1106         c.a = p->splat(1.0f);
1107     }
1108 
1109     // Alpha-only images get their color from the paint (already converted to dst color space).
1110     SkColorSpace* cs = upper->colorSpace();
1111     SkAlphaType   at = upper->alphaType();
1112     if (SkColorTypeIsAlphaOnly(upper->colorType())) {
1113         c.r = paint.r;
1114         c.g = paint.g;
1115         c.b = paint.b;
1116 
1117         cs = dst.colorSpace();
1118         at = kUnpremul_SkAlphaType;
1119     }
1120 
1121     if (sampling == SamplingEnum::kBicubic) {
1122         // Bicubic filtering naturally produces out of range values on both sides of [0,1].
1123         c.a = clamp01(c.a);
1124 
1125         skvm::F32 limit = (at == kUnpremul_SkAlphaType || fClampAsIfUnpremul)
1126                         ? p->splat(1.0f)
1127                         : c.a;
1128         c.r = clamp(c.r, 0.0f, limit);
1129         c.g = clamp(c.g, 0.0f, limit);
1130         c.b = clamp(c.b, 0.0f, limit);
1131     }
1132 
1133     return SkColorSpaceXformSteps{cs,at, dst.colorSpace(),dst.alphaType()}.program(p, uniforms, c);
1134 }
1135