• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/GrColorSpaceInfo.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     GrPixelConfig config = proxy->config();
232     bool isAlphaOnly = GrPixelConfigIsAlphaOnly(config);
233 
234     lmInverse.postScale(scaleAdjust[0], scaleAdjust[1]);
235 
236     std::unique_ptr<GrFragmentProcessor> inner;
237     if (doBicubic) {
238         // domainX and domainY will properly apply the decal effect with the texture domain used in
239         // the bicubic filter if clamp to border was unsupported in hardware
240         static constexpr auto kDir = GrBicubicEffect::Direction::kXY;
241         inner = GrBicubicEffect::Make(std::move(proxy), lmInverse, wrapModes, domainX, domainY,
242                                       kDir, fImage->alphaType());
243     } else {
244         if (domainX != GrTextureDomain::kIgnore_Mode || domainY != GrTextureDomain::kIgnore_Mode) {
245             SkRect domain = GrTextureDomain::MakeTexelDomain(
246                     SkIRect::MakeWH(proxy->width(), proxy->height()),
247                     domainX, domainY);
248             inner = GrTextureDomainEffect::Make(std::move(proxy), lmInverse, domain,
249                                                 domainX, domainY, samplerState);
250         } else {
251             inner = GrSimpleTextureEffect::Make(std::move(proxy), lmInverse, samplerState);
252         }
253     }
254     inner = GrColorSpaceXformEffect::Make(std::move(inner), fImage->colorSpace(),
255                                           fImage->alphaType(),
256                                           args.fDstColorSpaceInfo->colorSpace());
257     if (isAlphaOnly) {
258         return inner;
259     } else if (args.fInputColorIsOpaque) {
260         return GrFragmentProcessor::OverrideInput(std::move(inner), SK_PMColor4fWHITE, false);
261     }
262     return GrFragmentProcessor::MulChildByInputAlpha(std::move(inner));
263 }
264 
265 #endif
266 
267 ///////////////////////////////////////////////////////////////////////////////////////////////////
268 #include "src/core/SkImagePriv.h"
269 
SkMakeBitmapShader(const SkBitmap & src,SkTileMode tmx,SkTileMode tmy,const SkMatrix * localMatrix,SkCopyPixelsMode cpm)270 sk_sp<SkShader> SkMakeBitmapShader(const SkBitmap& src, SkTileMode tmx, SkTileMode tmy,
271                                    const SkMatrix* localMatrix, SkCopyPixelsMode cpm) {
272     return SkImageShader::Make(SkMakeImageFromRasterBitmap(src, cpm),
273                                tmx, tmy, localMatrix);
274 }
275 
SkMakeBitmapShaderForPaint(const SkPaint & paint,const SkBitmap & src,SkTileMode tmx,SkTileMode tmy,const SkMatrix * localMatrix,SkCopyPixelsMode mode)276 sk_sp<SkShader> SkMakeBitmapShaderForPaint(const SkPaint& paint, const SkBitmap& src,
277                                            SkTileMode tmx, SkTileMode tmy,
278                                            const SkMatrix* localMatrix, SkCopyPixelsMode mode) {
279     auto s = SkMakeBitmapShader(src, tmx, tmy, localMatrix, mode);
280     if (!s) {
281         return nullptr;
282     }
283     if (src.colorType() == kAlpha_8_SkColorType && paint.getShader()) {
284         // Compose the image shader with the paint's shader. Alpha images+shaders should output the
285         // texture's alpha multiplied by the shader's color. DstIn (d*sa) will achieve this with
286         // the source image and dst shader (MakeBlend takes dst first, src second).
287         s = SkShaders::Blend(SkBlendMode::kDstIn, paint.refShader(), std::move(s));
288     }
289     return s;
290 }
291 
RegisterFlattenables()292 void SkShaderBase::RegisterFlattenables() { SK_REGISTER_FLATTENABLE(SkImageShader); }
293 
294 class SkImageStageUpdater : public SkStageUpdater {
295 public:
296     const SkImageShader* fShader;
297 
298     float fMatrixStorage[6];
299 
300 #if 0   // TODO: when we support mipmaps
301     SkRasterPipeline_GatherCtx* fGather;
302     SkRasterPipeline_TileCtx* fLimitX;
303     SkRasterPipeline_TileCtx* fLimitY;
304     SkRasterPipeline_DecalTileCtx* fDecal;
305 #endif
306 
update(const SkMatrix & ctm,const SkMatrix * localM)307     bool update(const SkMatrix& ctm, const SkMatrix* localM) override {
308         SkMatrix matrix;
309         return fShader->computeTotalInverse(ctm, localM, &matrix) &&
310                matrix.asAffine(fMatrixStorage);
311     }
312 };
313 
doStages(const SkStageRec & rec,SkImageStageUpdater * updater) const314 bool SkImageShader::doStages(const SkStageRec& rec, SkImageStageUpdater* updater) const {
315     if (updater &&
316         (rec.fPaint.getFilterQuality() == kMedium_SkFilterQuality ||
317          rec.fCTM.hasPerspective()))
318     {
319         // TODO: handle these cases
320         // medium: recall RequestBitmap and update width/height accordingly
321         // perspt: store 9 floats and use persp stage
322         return false;
323     }
324 
325     SkRasterPipeline* p = rec.fPipeline;
326     SkArenaAlloc* alloc = rec.fAlloc;
327     auto quality = rec.fPaint.getFilterQuality();
328 
329     SkMatrix matrix;
330     if (!this->computeTotalInverse(rec.fCTM, rec.fLocalM, &matrix)) {
331         return false;
332     }
333 
334     const auto* state = SkBitmapController::RequestBitmap(as_IB(fImage.get()),
335                                                           matrix, quality, alloc);
336     if (!state) {
337         return false;
338     }
339 
340     const SkPixmap& pm = state->pixmap();
341     matrix  = state->invMatrix();
342     quality = state->quality();
343     auto info = pm.info();
344 
345     p->append(SkRasterPipeline::seed_shader);
346 
347     if (updater) {
348         p->append(SkRasterPipeline::matrix_2x3, updater->fMatrixStorage);
349     } else {
350         // When the matrix is just an integer translate, bilerp == nearest neighbor.
351         if (quality == kLow_SkFilterQuality &&
352             matrix.getType() <= SkMatrix::kTranslate_Mask &&
353             matrix.getTranslateX() == (int)matrix.getTranslateX() &&
354             matrix.getTranslateY() == (int)matrix.getTranslateY()) {
355             quality = kNone_SkFilterQuality;
356         }
357 
358         // See skia:4649 and the GM image_scale_aligned.
359         if (quality == kNone_SkFilterQuality) {
360             if (matrix.getScaleX() >= 0) {
361                 matrix.setTranslateX(nextafterf(matrix.getTranslateX(),
362                                                 floorf(matrix.getTranslateX())));
363             }
364             if (matrix.getScaleY() >= 0) {
365                 matrix.setTranslateY(nextafterf(matrix.getTranslateY(),
366                                                 floorf(matrix.getTranslateY())));
367             }
368         }
369         p->append_matrix(alloc, matrix);
370     }
371 
372     auto gather = alloc->make<SkRasterPipeline_GatherCtx>();
373     gather->pixels = pm.addr();
374     gather->stride = pm.rowBytesAsPixels();
375     gather->width  = pm.width();
376     gather->height = pm.height();
377 
378     auto limit_x = alloc->make<SkRasterPipeline_TileCtx>(),
379          limit_y = alloc->make<SkRasterPipeline_TileCtx>();
380     limit_x->scale = pm.width();
381     limit_x->invScale = 1.0f / pm.width();
382     limit_y->scale = pm.height();
383     limit_y->invScale = 1.0f / pm.height();
384 
385     SkRasterPipeline_DecalTileCtx* decal_ctx = nullptr;
386     bool decal_x_and_y = fTileModeX == SkTileMode::kDecal && fTileModeY == SkTileMode::kDecal;
387     if (fTileModeX == SkTileMode::kDecal || fTileModeY == SkTileMode::kDecal) {
388         decal_ctx = alloc->make<SkRasterPipeline_DecalTileCtx>();
389         decal_ctx->limit_x = limit_x->scale;
390         decal_ctx->limit_y = limit_y->scale;
391     }
392 
393 #if 0   // TODO: when we support kMedium
394     if (updator && (quality == kMedium_SkFilterQuality)) {
395         // if we change levels in mipmap, we need to update the scales (and invScales)
396         updator->fGather = gather;
397         updator->fLimitX = limit_x;
398         updator->fLimitY = limit_y;
399         updator->fDecal = decal_ctx;
400     }
401 #endif
402 
403     auto append_tiling_and_gather = [&] {
404         if (decal_x_and_y) {
405             p->append(SkRasterPipeline::decal_x_and_y,  decal_ctx);
406         } else {
407             switch (fTileModeX) {
408                 case SkTileMode::kClamp:  /* The gather_xxx stage will clamp for us. */     break;
409                 case SkTileMode::kMirror: p->append(SkRasterPipeline::mirror_x, limit_x);   break;
410                 case SkTileMode::kRepeat: p->append(SkRasterPipeline::repeat_x, limit_x);   break;
411                 case SkTileMode::kDecal:  p->append(SkRasterPipeline::decal_x,  decal_ctx); break;
412             }
413             switch (fTileModeY) {
414                 case SkTileMode::kClamp:  /* The gather_xxx stage will clamp for us. */     break;
415                 case SkTileMode::kMirror: p->append(SkRasterPipeline::mirror_y, limit_y);   break;
416                 case SkTileMode::kRepeat: p->append(SkRasterPipeline::repeat_y, limit_y);   break;
417                 case SkTileMode::kDecal:  p->append(SkRasterPipeline::decal_y,  decal_ctx); break;
418             }
419         }
420 
421         void* ctx = gather;
422         switch (info.colorType()) {
423             case kAlpha_8_SkColorType:      p->append(SkRasterPipeline::gather_a8,      ctx); break;
424             case kRGB_565_SkColorType:      p->append(SkRasterPipeline::gather_565,     ctx); break;
425             case kARGB_4444_SkColorType:    p->append(SkRasterPipeline::gather_4444,    ctx); break;
426             case kRGBA_8888_SkColorType:    p->append(SkRasterPipeline::gather_8888,    ctx); break;
427             case kRGBA_1010102_SkColorType: p->append(SkRasterPipeline::gather_1010102, ctx); break;
428             case kRGBA_F16Norm_SkColorType:
429             case kRGBA_F16_SkColorType:     p->append(SkRasterPipeline::gather_f16,     ctx); break;
430             case kRGBA_F32_SkColorType:     p->append(SkRasterPipeline::gather_f32,     ctx); break;
431 
432             case kGray_8_SkColorType:       p->append(SkRasterPipeline::gather_a8,      ctx);
433                                             p->append(SkRasterPipeline::alpha_to_gray      ); break;
434 
435             case kRGB_888x_SkColorType:     p->append(SkRasterPipeline::gather_8888,    ctx);
436                                             p->append(SkRasterPipeline::force_opaque       ); break;
437 
438             case kRGB_101010x_SkColorType:  p->append(SkRasterPipeline::gather_1010102, ctx);
439                                             p->append(SkRasterPipeline::force_opaque       ); break;
440 
441             case kBGRA_8888_SkColorType:    p->append(SkRasterPipeline::gather_8888,    ctx);
442                                             p->append(SkRasterPipeline::swap_rb            ); break;
443 
444             case kUnknown_SkColorType: SkASSERT(false);
445         }
446         if (decal_ctx) {
447             p->append(SkRasterPipeline::check_decal_mask, decal_ctx);
448         }
449     };
450 
451     auto append_misc = [&] {
452         // TODO: if ref.fDstCS isn't null, we'll premul here then immediately unpremul
453         // to do the color space transformation.  Might be possible to streamline.
454         if (info.colorType() == kAlpha_8_SkColorType) {
455             // The color for A8 images comes from the (sRGB) paint color.
456             p->append_set_rgb(alloc, rec.fPaint.getColor4f());
457             p->append(SkRasterPipeline::premul);
458         } else if (info.alphaType() == kUnpremul_SkAlphaType) {
459             // Convert unpremul images to premul before we carry on with the rest of the pipeline.
460             p->append(SkRasterPipeline::premul);
461         }
462 
463         if (quality > kLow_SkFilterQuality) {
464             // Bicubic filtering naturally produces out of range values on both sides.
465             p->append(SkRasterPipeline::clamp_0);
466             p->append(fClampAsIfUnpremul ? SkRasterPipeline::clamp_1
467                                          : SkRasterPipeline::clamp_a);
468         }
469 
470         if (rec.fDstCS) {
471             // If color managed, convert from premul source all the way to premul dst color space.
472             auto srcCS = info.colorSpace();
473             if (!srcCS || info.colorType() == kAlpha_8_SkColorType) {
474                 // We treat untagged images as sRGB.
475                 // A8 images get their r,g,b from the paint color, so they're also sRGB.
476                 srcCS = sk_srgb_singleton();
477             }
478             alloc->make<SkColorSpaceXformSteps>(srcCS     , kPremul_SkAlphaType,
479                                                 rec.fDstCS, kPremul_SkAlphaType)
480                 ->apply(p, info.colorType());
481         }
482 
483         return true;
484     };
485 
486     // Check for fast-path stages.
487     auto ct = info.colorType();
488     if (true
489         && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType)
490         && quality == kLow_SkFilterQuality
491         && fTileModeX == SkTileMode::kClamp && fTileModeY == SkTileMode::kClamp) {
492 
493         p->append(SkRasterPipeline::bilerp_clamp_8888, gather);
494         if (ct == kBGRA_8888_SkColorType) {
495             p->append(SkRasterPipeline::swap_rb);
496         }
497         return append_misc();
498     }
499     if (true
500         && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType) // TODO: all formats
501         && quality == kLow_SkFilterQuality
502         && fTileModeX != SkTileMode::kDecal // TODO decal too?
503         && fTileModeY != SkTileMode::kDecal) {
504 
505         auto ctx = alloc->make<SkRasterPipeline_SamplerCtx2>();
506         *(SkRasterPipeline_GatherCtx*)(ctx) = *gather;
507         ctx->ct = ct;
508         ctx->tileX = fTileModeX;
509         ctx->tileY = fTileModeY;
510         ctx->invWidth  = 1.0f / ctx->width;
511         ctx->invHeight = 1.0f / ctx->height;
512         p->append(SkRasterPipeline::bilinear, ctx);
513         return append_misc();
514     }
515     if (true
516         && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType)
517         && quality == kHigh_SkFilterQuality
518         && fTileModeX == SkTileMode::kClamp && fTileModeY == SkTileMode::kClamp) {
519 
520         p->append(SkRasterPipeline::bicubic_clamp_8888, gather);
521         if (ct == kBGRA_8888_SkColorType) {
522             p->append(SkRasterPipeline::swap_rb);
523         }
524         return append_misc();
525     }
526     if (true
527         && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType) // TODO: all formats
528         && quality == kHigh_SkFilterQuality
529         && fTileModeX != SkTileMode::kDecal // TODO decal too?
530         && fTileModeY != SkTileMode::kDecal) {
531 
532         auto ctx = alloc->make<SkRasterPipeline_SamplerCtx2>();
533         *(SkRasterPipeline_GatherCtx*)(ctx) = *gather;
534         ctx->ct = ct;
535         ctx->tileX = fTileModeX;
536         ctx->tileY = fTileModeY;
537         ctx->invWidth  = 1.0f / ctx->width;
538         ctx->invHeight = 1.0f / ctx->height;
539         p->append(SkRasterPipeline::bicubic, ctx);
540         return append_misc();
541     }
542 
543     SkRasterPipeline_SamplerCtx* sampler = nullptr;
544     if (quality != kNone_SkFilterQuality) {
545         sampler = alloc->make<SkRasterPipeline_SamplerCtx>();
546     }
547 
548     auto sample = [&](SkRasterPipeline::StockStage setup_x,
549                       SkRasterPipeline::StockStage setup_y) {
550         p->append(setup_x, sampler);
551         p->append(setup_y, sampler);
552         append_tiling_and_gather();
553         p->append(SkRasterPipeline::accumulate, sampler);
554     };
555 
556     if (quality == kNone_SkFilterQuality) {
557         append_tiling_and_gather();
558     } else if (quality == kLow_SkFilterQuality) {
559         p->append(SkRasterPipeline::save_xy, sampler);
560 
561         sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_ny);
562         sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_ny);
563         sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_py);
564         sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_py);
565 
566         p->append(SkRasterPipeline::move_dst_src);
567 
568     } else {
569         p->append(SkRasterPipeline::save_xy, sampler);
570 
571         sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n3y);
572         sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n3y);
573         sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n3y);
574         sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n3y);
575 
576         sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n1y);
577         sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n1y);
578         sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n1y);
579         sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n1y);
580 
581         sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p1y);
582         sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p1y);
583         sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p1y);
584         sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p1y);
585 
586         sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p3y);
587         sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p3y);
588         sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p3y);
589         sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p3y);
590 
591         p->append(SkRasterPipeline::move_dst_src);
592     }
593 
594     return append_misc();
595 }
596 
onAppendStages(const SkStageRec & rec) const597 bool SkImageShader::onAppendStages(const SkStageRec& rec) const {
598     return this->doStages(rec, nullptr);
599 }
600 
onAppendUpdatableStages(const SkStageRec & rec) const601 SkStageUpdater* SkImageShader::onAppendUpdatableStages(const SkStageRec& rec) const {
602     auto updater = rec.fAlloc->make<SkImageStageUpdater>();
603     updater->fShader = this;
604     return this->doStages(rec, updater) ? updater : nullptr;
605 }
606 
607