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/core/SkArenaAlloc.h"
9 #include "src/core/SkBitmapController.h"
10 #include "src/core/SkColorSpacePriv.h"
11 #include "src/core/SkColorSpaceXformSteps.h"
12 #include "src/core/SkRasterPipeline.h"
13 #include "src/core/SkReadBuffer.h"
14 #include "src/core/SkWriteBuffer.h"
15 #include "src/image/SkImage_Base.h"
16 #include "src/shaders/SkBitmapProcShader.h"
17 #include "src/shaders/SkEmptyShader.h"
18 #include "src/shaders/SkImageShader.h"
19 
20 /**
21  *  We are faster in clamp, so always use that tiling when we can.
22  */
optimize(SkTileMode tm,int dimension)23 static SkTileMode optimize(SkTileMode tm, int dimension) {
24     SkASSERT(dimension > 0);
25 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
26     // need to update frameworks/base/libs/hwui/tests/unit/SkiaBehaviorTests.cpp:55 to allow
27     // for transforming to clamp.
28     return tm;
29 #else
30     return dimension == 1 ? SkTileMode::kClamp : tm;
31 #endif
32 }
33 
SkImageShader(sk_sp<SkImage> img,SkTileMode tmx,SkTileMode tmy,const SkMatrix * localMatrix,bool clampAsIfUnpremul)34 SkImageShader::SkImageShader(sk_sp<SkImage> img,
35                              SkTileMode tmx, SkTileMode tmy,
36                              const SkMatrix* localMatrix,
37                              bool clampAsIfUnpremul)
38     : INHERITED(localMatrix)
39     , fImage(std::move(img))
40     , fTileModeX(optimize(tmx, fImage->width()))
41     , fTileModeY(optimize(tmy, fImage->height()))
42     , fClampAsIfUnpremul(clampAsIfUnpremul)
43 {}
44 
45 // fClampAsIfUnpremul is always false when constructed through public APIs,
46 // so there's no need to read or write it here.
47 
CreateProc(SkReadBuffer & buffer)48 sk_sp<SkFlattenable> SkImageShader::CreateProc(SkReadBuffer& buffer) {
49     auto tmx = buffer.read32LE<SkTileMode>(SkTileMode::kLastTileMode);
50     auto tmy = buffer.read32LE<SkTileMode>(SkTileMode::kLastTileMode);
51     SkMatrix localMatrix;
52     buffer.readMatrix(&localMatrix);
53     sk_sp<SkImage> img = buffer.readImage();
54     if (!img) {
55         return nullptr;
56     }
57     return SkImageShader::Make(std::move(img), tmx, tmy, &localMatrix);
58 }
59 
flatten(SkWriteBuffer & buffer) const60 void SkImageShader::flatten(SkWriteBuffer& buffer) const {
61     buffer.writeUInt((unsigned)fTileModeX);
62     buffer.writeUInt((unsigned)fTileModeY);
63     buffer.writeMatrix(this->getLocalMatrix());
64     buffer.writeImage(fImage.get());
65     SkASSERT(fClampAsIfUnpremul == false);
66 }
67 
isOpaque() const68 bool SkImageShader::isOpaque() const {
69     return fImage->isOpaque() &&
70            fTileModeX != SkTileMode::kDecal && fTileModeY != SkTileMode::kDecal;
71 }
72 
73 #ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
legacy_shader_can_handle(const SkMatrix & inv)74 static bool legacy_shader_can_handle(const SkMatrix& inv) {
75     if (!inv.isScaleTranslate()) {
76         return false;
77     }
78 
79     // legacy code uses SkFixed 32.32, so ensure the inverse doesn't map device coordinates
80     // out of range.
81     const SkScalar max_dev_coord = 32767.0f;
82     SkRect src;
83     SkAssertResult(inv.mapRect(&src, SkRect::MakeWH(max_dev_coord, max_dev_coord)));
84 
85     // take 1/4 of max signed 32bits so we have room to subtract local values
86     const SkScalar max_fixed32dot32 = SK_MaxS32 * 0.25f;
87     if (!SkRect::MakeLTRB(-max_fixed32dot32, -max_fixed32dot32,
88                            max_fixed32dot32, max_fixed32dot32).contains(src)) {
89         return false;
90     }
91 
92     // legacy shader impl should be able to handle these matrices
93     return true;
94 }
95 
onMakeContext(const ContextRec & rec,SkArenaAlloc * alloc) const96 SkShaderBase::Context* SkImageShader::onMakeContext(const ContextRec& rec,
97                                                     SkArenaAlloc* alloc) const {
98     if (fImage->alphaType() == kUnpremul_SkAlphaType) {
99         return nullptr;
100     }
101     if (fImage->colorType() != kN32_SkColorType) {
102         return nullptr;
103     }
104     if (fTileModeX != fTileModeY) {
105         return nullptr;
106     }
107     if (fTileModeX == SkTileMode::kDecal || fTileModeY == SkTileMode::kDecal) {
108         return nullptr;
109     }
110 
111     // SkBitmapProcShader stores bitmap coordinates in a 16bit buffer,
112     // so it can't handle bitmaps larger than 65535.
113     //
114     // We back off another bit to 32767 to make small amounts of
115     // intermediate math safe, e.g. in
116     //
117     //     SkFixed fx = ...;
118     //     fx = tile(fx + SK_Fixed1);
119     //
120     // we want to make sure (fx + SK_Fixed1) never overflows.
121     if (fImage-> width() > 32767 ||
122         fImage->height() > 32767) {
123         return nullptr;
124     }
125 
126     SkMatrix inv;
127     if (!this->computeTotalInverse(*rec.fMatrix, rec.fLocalMatrix, &inv) ||
128         !legacy_shader_can_handle(inv)) {
129         return nullptr;
130     }
131 
132     if (!rec.isLegacyCompatible(fImage->colorSpace())) {
133         return nullptr;
134     }
135 
136     return SkBitmapProcLegacyShader::MakeContext(*this, fTileModeX, fTileModeY,
137                                                  as_IB(fImage.get()), rec, alloc);
138 }
139 #endif
140 
onIsAImage(SkMatrix * texM,SkTileMode xy[]) const141 SkImage* SkImageShader::onIsAImage(SkMatrix* texM, SkTileMode xy[]) const {
142     if (texM) {
143         *texM = this->getLocalMatrix();
144     }
145     if (xy) {
146         xy[0] = fTileModeX;
147         xy[1] = fTileModeY;
148     }
149     return const_cast<SkImage*>(fImage.get());
150 }
151 
Make(sk_sp<SkImage> image,SkTileMode tmx,SkTileMode tmy,const SkMatrix * localMatrix,bool clampAsIfUnpremul)152 sk_sp<SkShader> SkImageShader::Make(sk_sp<SkImage> image,
153                                     SkTileMode tmx, SkTileMode tmy,
154                                     const SkMatrix* localMatrix,
155                                     bool clampAsIfUnpremul) {
156     if (!image) {
157         return sk_make_sp<SkEmptyShader>();
158     }
159     return sk_sp<SkShader>{ new SkImageShader(image, tmx, tmy, localMatrix, clampAsIfUnpremul) };
160 }
161 
162 ///////////////////////////////////////////////////////////////////////////////////////////////////
163 
164 #if SK_SUPPORT_GPU
165 
166 #include "include/private/GrRecordingContext.h"
167 #include "src/gpu/GrCaps.h"
168 #include "src/gpu/GrColorInfo.h"
169 #include "src/gpu/GrRecordingContextPriv.h"
170 #include "src/gpu/SkGr.h"
171 #include "src/gpu/effects/GrBicubicEffect.h"
172 #include "src/gpu/effects/generated/GrSimpleTextureEffect.h"
173 
tile_mode_to_wrap_mode(const SkTileMode tileMode)174 static GrSamplerState::WrapMode tile_mode_to_wrap_mode(const SkTileMode tileMode) {
175     switch (tileMode) {
176         case SkTileMode::kClamp:
177             return GrSamplerState::WrapMode::kClamp;
178         case SkTileMode::kRepeat:
179             return GrSamplerState::WrapMode::kRepeat;
180         case SkTileMode::kMirror:
181             return GrSamplerState::WrapMode::kMirrorRepeat;
182         case SkTileMode::kDecal:
183             return GrSamplerState::WrapMode::kClampToBorder;
184     }
185     SK_ABORT("Unknown tile mode.");
186 }
187 
asFragmentProcessor(const GrFPArgs & args) const188 std::unique_ptr<GrFragmentProcessor> SkImageShader::asFragmentProcessor(
189         const GrFPArgs& args) const {
190     const auto lm = this->totalLocalMatrix(args.fPreLocalMatrix, args.fPostLocalMatrix);
191     SkMatrix lmInverse;
192     if (!lm->invert(&lmInverse)) {
193         return nullptr;
194     }
195 
196     GrSamplerState::WrapMode wrapModes[] = {tile_mode_to_wrap_mode(fTileModeX),
197                                             tile_mode_to_wrap_mode(fTileModeY)};
198 
199     // If either domainX or domainY are un-ignored, a texture domain effect has to be used to
200     // implement the decal mode (while leaving non-decal axes alone). The wrap mode originally
201     // clamp-to-border is reset to clamp since the hw cannot implement it directly.
202     GrTextureDomain::Mode domainX = GrTextureDomain::kIgnore_Mode;
203     GrTextureDomain::Mode domainY = GrTextureDomain::kIgnore_Mode;
204     if (!args.fContext->priv().caps()->clampToBorderSupport()) {
205         if (wrapModes[0] == GrSamplerState::WrapMode::kClampToBorder) {
206             domainX = GrTextureDomain::kDecal_Mode;
207             wrapModes[0] = GrSamplerState::WrapMode::kClamp;
208         }
209         if (wrapModes[1] == GrSamplerState::WrapMode::kClampToBorder) {
210             domainY = GrTextureDomain::kDecal_Mode;
211             wrapModes[1] = GrSamplerState::WrapMode::kClamp;
212         }
213     }
214 
215     // Must set wrap and filter on the sampler before requesting a texture. In two places below
216     // we check the matrix scale factors to determine how to interpret the filter quality setting.
217     // This completely ignores the complexity of the drawVertices case where explicit local coords
218     // are provided by the caller.
219     bool doBicubic;
220     GrSamplerState::Filter textureFilterMode = GrSkFilterQualityToGrFilterMode(
221             fImage->width(), fImage->height(), args.fFilterQuality, *args.fViewMatrix, *lm,
222             args.fContext->priv().options().fSharpenMipmappedTextures, &doBicubic);
223     GrSamplerState samplerState(wrapModes, textureFilterMode);
224     SkScalar scaleAdjust[2] = { 1.0f, 1.0f };
225     sk_sp<GrTextureProxy> proxy(as_IB(fImage)->asTextureProxyRef(args.fContext, samplerState,
226                                                                  scaleAdjust));
227     if (!proxy) {
228         return nullptr;
229     }
230 
231     GrColorType srcColorType = SkColorTypeToGrColorType(fImage->colorType());
232 
233     lmInverse.postScale(scaleAdjust[0], scaleAdjust[1]);
234 
235     std::unique_ptr<GrFragmentProcessor> inner;
236     if (doBicubic) {
237         // domainX and domainY will properly apply the decal effect with the texture domain used in
238         // the bicubic filter if clamp to border was unsupported in hardware
239         static constexpr auto kDir = GrBicubicEffect::Direction::kXY;
240         inner = GrBicubicEffect::Make(std::move(proxy), srcColorType, lmInverse, wrapModes, domainX,
241                                       domainY, kDir, fImage->alphaType());
242     } else {
243         if (domainX != GrTextureDomain::kIgnore_Mode || domainY != GrTextureDomain::kIgnore_Mode) {
244             SkRect domain = GrTextureDomain::MakeTexelDomain(
245                     SkIRect::MakeWH(proxy->width(), proxy->height()),
246                     domainX, domainY);
247             inner = GrTextureDomainEffect::Make(std::move(proxy), srcColorType, lmInverse, domain,
248                                                 domainX, domainY, samplerState);
249         } else {
250             inner = GrSimpleTextureEffect::Make(std::move(proxy), srcColorType, lmInverse,
251                                                 samplerState);
252         }
253     }
254     inner = GrColorSpaceXformEffect::Make(std::move(inner), fImage->colorSpace(),
255                                           fImage->alphaType(), args.fDstColorInfo->colorSpace());
256 
257     bool isAlphaOnly = SkColorTypeIsAlphaOnly(fImage->colorType());
258     if (isAlphaOnly) {
259         return inner;
260     } else if (args.fInputColorIsOpaque) {
261         return GrFragmentProcessor::OverrideInput(std::move(inner), SK_PMColor4fWHITE, false);
262     }
263     return GrFragmentProcessor::MulChildByInputAlpha(std::move(inner));
264 }
265 
266 #endif
267 
268 ///////////////////////////////////////////////////////////////////////////////////////////////////
269 #include "src/core/SkImagePriv.h"
270 
SkMakeBitmapShader(const SkBitmap & src,SkTileMode tmx,SkTileMode tmy,const SkMatrix * localMatrix,SkCopyPixelsMode cpm)271 sk_sp<SkShader> SkMakeBitmapShader(const SkBitmap& src, SkTileMode tmx, SkTileMode tmy,
272                                    const SkMatrix* localMatrix, SkCopyPixelsMode cpm) {
273     return SkImageShader::Make(SkMakeImageFromRasterBitmap(src, cpm),
274                                tmx, tmy, localMatrix);
275 }
276 
SkMakeBitmapShaderForPaint(const SkPaint & paint,const SkBitmap & src,SkTileMode tmx,SkTileMode tmy,const SkMatrix * localMatrix,SkCopyPixelsMode mode)277 sk_sp<SkShader> SkMakeBitmapShaderForPaint(const SkPaint& paint, const SkBitmap& src,
278                                            SkTileMode tmx, SkTileMode tmy,
279                                            const SkMatrix* localMatrix, SkCopyPixelsMode mode) {
280     auto s = SkMakeBitmapShader(src, tmx, tmy, localMatrix, mode);
281     if (!s) {
282         return nullptr;
283     }
284     if (src.colorType() == kAlpha_8_SkColorType && paint.getShader()) {
285         // Compose the image shader with the paint's shader. Alpha images+shaders should output the
286         // texture's alpha multiplied by the shader's color. DstIn (d*sa) will achieve this with
287         // the source image and dst shader (MakeBlend takes dst first, src second).
288         s = SkShaders::Blend(SkBlendMode::kDstIn, paint.refShader(), std::move(s));
289     }
290     return s;
291 }
292 
RegisterFlattenables()293 void SkShaderBase::RegisterFlattenables() { SK_REGISTER_FLATTENABLE(SkImageShader); }
294 
295 class SkImageStageUpdater : public SkStageUpdater {
296 public:
297     const SkImageShader* fShader;
298 
299     float fMatrixStorage[6];
300 
301 #if 0   // TODO: when we support mipmaps
302     SkRasterPipeline_GatherCtx* fGather;
303     SkRasterPipeline_TileCtx* fLimitX;
304     SkRasterPipeline_TileCtx* fLimitY;
305     SkRasterPipeline_DecalTileCtx* fDecal;
306 #endif
307 
update(const SkMatrix & ctm,const SkMatrix * localM)308     bool update(const SkMatrix& ctm, const SkMatrix* localM) override {
309         SkMatrix matrix;
310         return fShader->computeTotalInverse(ctm, localM, &matrix) &&
311                matrix.asAffine(fMatrixStorage);
312     }
313 };
314 
doStages(const SkStageRec & rec,SkImageStageUpdater * updater) const315 bool SkImageShader::doStages(const SkStageRec& rec, SkImageStageUpdater* updater) const {
316     if (updater &&
317         (rec.fPaint.getFilterQuality() == kMedium_SkFilterQuality ||
318          rec.fCTM.hasPerspective()))
319     {
320         // TODO: handle these cases
321         // medium: recall RequestBitmap and update width/height accordingly
322         // perspt: store 9 floats and use persp stage
323         return false;
324     }
325 
326     SkRasterPipeline* p = rec.fPipeline;
327     SkArenaAlloc* alloc = rec.fAlloc;
328     auto quality = rec.fPaint.getFilterQuality();
329 
330     SkMatrix matrix;
331     if (!this->computeTotalInverse(rec.fCTM, rec.fLocalM, &matrix)) {
332         return false;
333     }
334 
335     const auto* state = SkBitmapController::RequestBitmap(as_IB(fImage.get()),
336                                                           matrix, quality, alloc);
337     if (!state) {
338         return false;
339     }
340 
341     const SkPixmap& pm = state->pixmap();
342     matrix  = state->invMatrix();
343     quality = state->quality();
344     auto info = pm.info();
345 
346     p->append(SkRasterPipeline::seed_shader);
347 
348     if (updater) {
349         p->append(SkRasterPipeline::matrix_2x3, updater->fMatrixStorage);
350     } else {
351         // When the matrix is just an integer translate, bilerp == nearest neighbor.
352         if (quality == kLow_SkFilterQuality &&
353             matrix.getType() <= SkMatrix::kTranslate_Mask &&
354             matrix.getTranslateX() == (int)matrix.getTranslateX() &&
355             matrix.getTranslateY() == (int)matrix.getTranslateY()) {
356             quality = kNone_SkFilterQuality;
357         }
358 
359         // See skia:4649 and the GM image_scale_aligned.
360         if (quality == kNone_SkFilterQuality) {
361             if (matrix.getScaleX() >= 0) {
362                 matrix.setTranslateX(nextafterf(matrix.getTranslateX(),
363                                                 floorf(matrix.getTranslateX())));
364             }
365             if (matrix.getScaleY() >= 0) {
366                 matrix.setTranslateY(nextafterf(matrix.getTranslateY(),
367                                                 floorf(matrix.getTranslateY())));
368             }
369         }
370         p->append_matrix(alloc, matrix);
371     }
372 
373     auto gather = alloc->make<SkRasterPipeline_GatherCtx>();
374     gather->pixels = pm.addr();
375     gather->stride = pm.rowBytesAsPixels();
376     gather->width  = pm.width();
377     gather->height = pm.height();
378 
379     auto limit_x = alloc->make<SkRasterPipeline_TileCtx>(),
380          limit_y = alloc->make<SkRasterPipeline_TileCtx>();
381     limit_x->scale = pm.width();
382     limit_x->invScale = 1.0f / pm.width();
383     limit_y->scale = pm.height();
384     limit_y->invScale = 1.0f / pm.height();
385 
386     SkRasterPipeline_DecalTileCtx* decal_ctx = nullptr;
387     bool decal_x_and_y = fTileModeX == SkTileMode::kDecal && fTileModeY == SkTileMode::kDecal;
388     if (fTileModeX == SkTileMode::kDecal || fTileModeY == SkTileMode::kDecal) {
389         decal_ctx = alloc->make<SkRasterPipeline_DecalTileCtx>();
390         decal_ctx->limit_x = limit_x->scale;
391         decal_ctx->limit_y = limit_y->scale;
392     }
393 
394 #if 0   // TODO: when we support kMedium
395     if (updator && (quality == kMedium_SkFilterQuality)) {
396         // if we change levels in mipmap, we need to update the scales (and invScales)
397         updator->fGather = gather;
398         updator->fLimitX = limit_x;
399         updator->fLimitY = limit_y;
400         updator->fDecal = decal_ctx;
401     }
402 #endif
403 
404     auto append_tiling_and_gather = [&] {
405         if (decal_x_and_y) {
406             p->append(SkRasterPipeline::decal_x_and_y,  decal_ctx);
407         } else {
408             switch (fTileModeX) {
409                 case SkTileMode::kClamp:  /* The gather_xxx stage will clamp for us. */     break;
410                 case SkTileMode::kMirror: p->append(SkRasterPipeline::mirror_x, limit_x);   break;
411                 case SkTileMode::kRepeat: p->append(SkRasterPipeline::repeat_x, limit_x);   break;
412                 case SkTileMode::kDecal:  p->append(SkRasterPipeline::decal_x,  decal_ctx); break;
413             }
414             switch (fTileModeY) {
415                 case SkTileMode::kClamp:  /* The gather_xxx stage will clamp for us. */     break;
416                 case SkTileMode::kMirror: p->append(SkRasterPipeline::mirror_y, limit_y);   break;
417                 case SkTileMode::kRepeat: p->append(SkRasterPipeline::repeat_y, limit_y);   break;
418                 case SkTileMode::kDecal:  p->append(SkRasterPipeline::decal_y,  decal_ctx); break;
419             }
420         }
421 
422         void* ctx = gather;
423         switch (info.colorType()) {
424             case kAlpha_8_SkColorType:      p->append(SkRasterPipeline::gather_a8,      ctx); break;
425             case kA16_unorm_SkColorType:    p->append(SkRasterPipeline::gather_a16,     ctx); break;
426             case kA16_float_SkColorType:    p->append(SkRasterPipeline::gather_af16,    ctx); break;
427             case kRGB_565_SkColorType:      p->append(SkRasterPipeline::gather_565,     ctx); break;
428             case kARGB_4444_SkColorType:    p->append(SkRasterPipeline::gather_4444,    ctx); break;
429             case kR8G8_unorm_SkColorType:   p->append(SkRasterPipeline::gather_rg88,    ctx); break;
430             case kR16G16_unorm_SkColorType: p->append(SkRasterPipeline::gather_rg1616,  ctx); break;
431             case kR16G16_float_SkColorType: p->append(SkRasterPipeline::gather_rgf16,  ctx);  break;
432             case kRGBA_8888_SkColorType:    p->append(SkRasterPipeline::gather_8888,    ctx); break;
433             case kRGBA_1010102_SkColorType: p->append(SkRasterPipeline::gather_1010102, ctx); break;
434             case kR16G16B16A16_unorm_SkColorType:
435                                             p->append(SkRasterPipeline::gather_16161616,ctx); break;
436             case kRGBA_F16Norm_SkColorType:
437             case kRGBA_F16_SkColorType:     p->append(SkRasterPipeline::gather_f16,     ctx); break;
438             case kRGBA_F32_SkColorType:     p->append(SkRasterPipeline::gather_f32,     ctx); break;
439 
440             case kGray_8_SkColorType:       p->append(SkRasterPipeline::gather_a8,      ctx);
441                                             p->append(SkRasterPipeline::alpha_to_gray      ); break;
442 
443             case kRGB_888x_SkColorType:     p->append(SkRasterPipeline::gather_8888,    ctx);
444                                             p->append(SkRasterPipeline::force_opaque       ); break;
445 
446             case kRGB_101010x_SkColorType:  p->append(SkRasterPipeline::gather_1010102, ctx);
447                                             p->append(SkRasterPipeline::force_opaque       ); break;
448 
449             case kBGRA_8888_SkColorType:    p->append(SkRasterPipeline::gather_8888,    ctx);
450                                             p->append(SkRasterPipeline::swap_rb            ); break;
451 
452             case kUnknown_SkColorType: SkASSERT(false);
453         }
454         if (decal_ctx) {
455             p->append(SkRasterPipeline::check_decal_mask, decal_ctx);
456         }
457     };
458 
459     auto append_misc = [&] {
460         // TODO: if ref.fDstCS isn't null, we'll premul here then immediately unpremul
461         // to do the color space transformation.  Might be possible to streamline.
462         if (info.colorType() == kAlpha_8_SkColorType) {
463             // The color for A8 images comes from the (sRGB) paint color.
464             p->append_set_rgb(alloc, rec.fPaint.getColor4f());
465             p->append(SkRasterPipeline::premul);
466         } else if (info.alphaType() == kUnpremul_SkAlphaType) {
467             // Convert unpremul images to premul before we carry on with the rest of the pipeline.
468             p->append(SkRasterPipeline::premul);
469         }
470 
471         if (quality > kLow_SkFilterQuality) {
472             // Bicubic filtering naturally produces out of range values on both sides.
473             p->append(SkRasterPipeline::clamp_0);
474             p->append(fClampAsIfUnpremul ? SkRasterPipeline::clamp_1
475                                          : SkRasterPipeline::clamp_a);
476         }
477 
478         if (rec.fDstCS) {
479             // If color managed, convert from premul source all the way to premul dst color space.
480             auto srcCS = info.colorSpace();
481             if (!srcCS || info.colorType() == kAlpha_8_SkColorType) {
482                 // We treat untagged images as sRGB.
483                 // A8 images get their r,g,b from the paint color, so they're also sRGB.
484                 srcCS = sk_srgb_singleton();
485             }
486             alloc->make<SkColorSpaceXformSteps>(srcCS     , kPremul_SkAlphaType,
487                                                 rec.fDstCS, kPremul_SkAlphaType)
488                 ->apply(p, info.colorType());
489         }
490 
491         return true;
492     };
493 
494     // Check for fast-path stages.
495     auto ct = info.colorType();
496     if (true
497         && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType)
498         && quality == kLow_SkFilterQuality
499         && fTileModeX == SkTileMode::kClamp && fTileModeY == SkTileMode::kClamp) {
500 
501         p->append(SkRasterPipeline::bilerp_clamp_8888, gather);
502         if (ct == kBGRA_8888_SkColorType) {
503             p->append(SkRasterPipeline::swap_rb);
504         }
505         return append_misc();
506     }
507     if (true
508         && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType) // TODO: all formats
509         && quality == kLow_SkFilterQuality
510         && fTileModeX != SkTileMode::kDecal // TODO decal too?
511         && fTileModeY != SkTileMode::kDecal) {
512 
513         auto ctx = alloc->make<SkRasterPipeline_SamplerCtx2>();
514         *(SkRasterPipeline_GatherCtx*)(ctx) = *gather;
515         ctx->ct = ct;
516         ctx->tileX = fTileModeX;
517         ctx->tileY = fTileModeY;
518         ctx->invWidth  = 1.0f / ctx->width;
519         ctx->invHeight = 1.0f / ctx->height;
520         p->append(SkRasterPipeline::bilinear, ctx);
521         return append_misc();
522     }
523     if (true
524         && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType)
525         && quality == kHigh_SkFilterQuality
526         && fTileModeX == SkTileMode::kClamp && fTileModeY == SkTileMode::kClamp) {
527 
528         p->append(SkRasterPipeline::bicubic_clamp_8888, gather);
529         if (ct == kBGRA_8888_SkColorType) {
530             p->append(SkRasterPipeline::swap_rb);
531         }
532         return append_misc();
533     }
534     if (true
535         && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType) // TODO: all formats
536         && quality == kHigh_SkFilterQuality
537         && fTileModeX != SkTileMode::kDecal // TODO decal too?
538         && fTileModeY != SkTileMode::kDecal) {
539 
540         auto ctx = alloc->make<SkRasterPipeline_SamplerCtx2>();
541         *(SkRasterPipeline_GatherCtx*)(ctx) = *gather;
542         ctx->ct = ct;
543         ctx->tileX = fTileModeX;
544         ctx->tileY = fTileModeY;
545         ctx->invWidth  = 1.0f / ctx->width;
546         ctx->invHeight = 1.0f / ctx->height;
547         p->append(SkRasterPipeline::bicubic, ctx);
548         return append_misc();
549     }
550 
551     SkRasterPipeline_SamplerCtx* sampler = nullptr;
552     if (quality != kNone_SkFilterQuality) {
553         sampler = alloc->make<SkRasterPipeline_SamplerCtx>();
554     }
555 
556     auto sample = [&](SkRasterPipeline::StockStage setup_x,
557                       SkRasterPipeline::StockStage setup_y) {
558         p->append(setup_x, sampler);
559         p->append(setup_y, sampler);
560         append_tiling_and_gather();
561         p->append(SkRasterPipeline::accumulate, sampler);
562     };
563 
564     if (quality == kNone_SkFilterQuality) {
565         append_tiling_and_gather();
566     } else if (quality == kLow_SkFilterQuality) {
567         p->append(SkRasterPipeline::save_xy, sampler);
568 
569         sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_ny);
570         sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_ny);
571         sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_py);
572         sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_py);
573 
574         p->append(SkRasterPipeline::move_dst_src);
575 
576     } else {
577         p->append(SkRasterPipeline::save_xy, sampler);
578 
579         sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n3y);
580         sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n3y);
581         sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n3y);
582         sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n3y);
583 
584         sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n1y);
585         sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n1y);
586         sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n1y);
587         sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n1y);
588 
589         sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p1y);
590         sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p1y);
591         sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p1y);
592         sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p1y);
593 
594         sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p3y);
595         sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p3y);
596         sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p3y);
597         sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p3y);
598 
599         p->append(SkRasterPipeline::move_dst_src);
600     }
601 
602     return append_misc();
603 }
604 
onAppendStages(const SkStageRec & rec) const605 bool SkImageShader::onAppendStages(const SkStageRec& rec) const {
606     return this->doStages(rec, nullptr);
607 }
608 
onAppendUpdatableStages(const SkStageRec & rec) const609 SkStageUpdater* SkImageShader::onAppendUpdatableStages(const SkStageRec& rec) const {
610     auto updater = rec.fAlloc->make<SkImageStageUpdater>();
611     updater->fShader = this;
612     return this->doStages(rec, updater) ? updater : nullptr;
613 }
614 
615