• 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/SkOpts.h"
13 #include "src/core/SkRasterPipeline.h"
14 #include "src/core/SkReadBuffer.h"
15 #include "src/core/SkVM.h"
16 #include "src/core/SkWriteBuffer.h"
17 #include "src/image/SkImage_Base.h"
18 #include "src/shaders/SkBitmapProcShader.h"
19 #include "src/shaders/SkEmptyShader.h"
20 #include "src/shaders/SkImageShader.h"
21 
22 /**
23  *  We are faster in clamp, so always use that tiling when we can.
24  */
optimize(SkTileMode tm,int dimension)25 static SkTileMode optimize(SkTileMode tm, int dimension) {
26     SkASSERT(dimension > 0);
27 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
28     // need to update frameworks/base/libs/hwui/tests/unit/SkiaBehaviorTests.cpp:55 to allow
29     // for transforming to clamp.
30     return tm;
31 #else
32     return dimension == 1 ? SkTileMode::kClamp : tm;
33 #endif
34 }
35 
SkImageShader(sk_sp<SkImage> img,SkTileMode tmx,SkTileMode tmy,const SkMatrix * localMatrix,bool clampAsIfUnpremul)36 SkImageShader::SkImageShader(sk_sp<SkImage> img,
37                              SkTileMode tmx, SkTileMode tmy,
38                              const SkMatrix* localMatrix,
39                              bool clampAsIfUnpremul)
40     : INHERITED(localMatrix)
41     , fImage(std::move(img))
42     , fTileModeX(optimize(tmx, fImage->width()))
43     , fTileModeY(optimize(tmy, fImage->height()))
44     , fClampAsIfUnpremul(clampAsIfUnpremul)
45 {}
46 
47 // fClampAsIfUnpremul is always false when constructed through public APIs,
48 // so there's no need to read or write it here.
49 
CreateProc(SkReadBuffer & buffer)50 sk_sp<SkFlattenable> SkImageShader::CreateProc(SkReadBuffer& buffer) {
51     auto tmx = buffer.read32LE<SkTileMode>(SkTileMode::kLastTileMode);
52     auto tmy = buffer.read32LE<SkTileMode>(SkTileMode::kLastTileMode);
53     SkMatrix localMatrix;
54     buffer.readMatrix(&localMatrix);
55     sk_sp<SkImage> img = buffer.readImage();
56     if (!img) {
57         return nullptr;
58     }
59     return SkImageShader::Make(std::move(img), tmx, tmy, &localMatrix);
60 }
61 
flatten(SkWriteBuffer & buffer) const62 void SkImageShader::flatten(SkWriteBuffer& buffer) const {
63     buffer.writeUInt((unsigned)fTileModeX);
64     buffer.writeUInt((unsigned)fTileModeY);
65     buffer.writeMatrix(this->getLocalMatrix());
66     buffer.writeImage(fImage.get());
67     SkASSERT(fClampAsIfUnpremul == false);
68 }
69 
isOpaque() const70 bool SkImageShader::isOpaque() const {
71     return fImage->isOpaque() &&
72            fTileModeX != SkTileMode::kDecal && fTileModeY != SkTileMode::kDecal;
73 }
74 
75 #ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
legacy_shader_can_handle(const SkMatrix & inv)76 static bool legacy_shader_can_handle(const SkMatrix& inv) {
77     if (inv.hasPerspective()) {
78         return false;
79     }
80 
81     // Scale+translate methods are always present, but affine might not be.
82     if (!SkOpts::S32_alpha_D32_filter_DXDY && !inv.isScaleTranslate()) {
83         return false;
84     }
85 
86     // legacy code uses SkFixed 32.32, so ensure the inverse doesn't map device coordinates
87     // out of range.
88     const SkScalar max_dev_coord = 32767.0f;
89     const SkRect src = inv.mapRect(SkRect::MakeWH(max_dev_coord, max_dev_coord));
90 
91     // take 1/4 of max signed 32bits so we have room to subtract local values
92     const SkScalar max_fixed32dot32 = float(SK_MaxS32) * 0.25f;
93     if (!SkRect::MakeLTRB(-max_fixed32dot32, -max_fixed32dot32,
94                           +max_fixed32dot32, +max_fixed32dot32).contains(src)) {
95         return false;
96     }
97 
98     // legacy shader impl should be able to handle these matrices
99     return true;
100 }
101 
onMakeContext(const ContextRec & rec,SkArenaAlloc * alloc) const102 SkShaderBase::Context* SkImageShader::onMakeContext(const ContextRec& rec,
103                                                     SkArenaAlloc* alloc) const {
104     if (fImage->alphaType() == kUnpremul_SkAlphaType) {
105         return nullptr;
106     }
107     if (fImage->colorType() != kN32_SkColorType) {
108         return nullptr;
109     }
110 #if !defined(SK_SUPPORT_LEGACY_TILED_BITMAPS)
111     if (fTileModeX != fTileModeY) {
112         return nullptr;
113     }
114 #endif
115     if (fTileModeX == SkTileMode::kDecal || fTileModeY == SkTileMode::kDecal) {
116         return nullptr;
117     }
118 
119     // SkBitmapProcShader stores bitmap coordinates in a 16bit buffer,
120     // so it can't handle bitmaps larger than 65535.
121     //
122     // We back off another bit to 32767 to make small amounts of
123     // intermediate math safe, e.g. in
124     //
125     //     SkFixed fx = ...;
126     //     fx = tile(fx + SK_Fixed1);
127     //
128     // we want to make sure (fx + SK_Fixed1) never overflows.
129     if (fImage-> width() > 32767 ||
130         fImage->height() > 32767) {
131         return nullptr;
132     }
133 
134     SkMatrix inv;
135     if (!this->computeTotalInverse(*rec.fMatrix, rec.fLocalMatrix, &inv) ||
136         !legacy_shader_can_handle(inv)) {
137         return nullptr;
138     }
139 
140     if (!rec.isLegacyCompatible(fImage->colorSpace())) {
141         return nullptr;
142     }
143 
144     return SkBitmapProcLegacyShader::MakeContext(*this, fTileModeX, fTileModeY,
145                                                  as_IB(fImage.get()), rec, alloc);
146 }
147 #endif
148 
onIsAImage(SkMatrix * texM,SkTileMode xy[]) const149 SkImage* SkImageShader::onIsAImage(SkMatrix* texM, SkTileMode xy[]) const {
150     if (texM) {
151         *texM = this->getLocalMatrix();
152     }
153     if (xy) {
154         xy[0] = fTileModeX;
155         xy[1] = fTileModeY;
156     }
157     return const_cast<SkImage*>(fImage.get());
158 }
159 
Make(sk_sp<SkImage> image,SkTileMode tmx,SkTileMode tmy,const SkMatrix * localMatrix,bool clampAsIfUnpremul)160 sk_sp<SkShader> SkImageShader::Make(sk_sp<SkImage> image,
161                                     SkTileMode tmx, SkTileMode tmy,
162                                     const SkMatrix* localMatrix,
163                                     bool clampAsIfUnpremul) {
164     if (!image) {
165         return sk_make_sp<SkEmptyShader>();
166     }
167     return sk_sp<SkShader>{ new SkImageShader(image, tmx, tmy, localMatrix, clampAsIfUnpremul) };
168 }
169 
170 ///////////////////////////////////////////////////////////////////////////////////////////////////
171 
172 #if SK_SUPPORT_GPU
173 
174 #include "include/private/GrRecordingContext.h"
175 #include "src/gpu/GrCaps.h"
176 #include "src/gpu/GrColorInfo.h"
177 #include "src/gpu/GrRecordingContextPriv.h"
178 #include "src/gpu/SkGr.h"
179 #include "src/gpu/effects/GrBicubicEffect.h"
180 #include "src/gpu/effects/GrTextureEffect.h"
181 
tile_mode_to_wrap_mode(const SkTileMode tileMode)182 static GrSamplerState::WrapMode tile_mode_to_wrap_mode(const SkTileMode tileMode) {
183     switch (tileMode) {
184         case SkTileMode::kClamp:
185             return GrSamplerState::WrapMode::kClamp;
186         case SkTileMode::kRepeat:
187             return GrSamplerState::WrapMode::kRepeat;
188         case SkTileMode::kMirror:
189             return GrSamplerState::WrapMode::kMirrorRepeat;
190         case SkTileMode::kDecal:
191             return GrSamplerState::WrapMode::kClampToBorder;
192     }
193     SK_ABORT("Unknown tile mode.");
194 }
195 
asFragmentProcessor(const GrFPArgs & args) const196 std::unique_ptr<GrFragmentProcessor> SkImageShader::asFragmentProcessor(
197         const GrFPArgs& args) const {
198     const auto lm = this->totalLocalMatrix(args.fPreLocalMatrix, args.fPostLocalMatrix);
199     SkMatrix lmInverse;
200     if (!lm->invert(&lmInverse)) {
201         return nullptr;
202     }
203 
204     GrSamplerState::WrapMode wm[] = {tile_mode_to_wrap_mode(fTileModeX),
205                                      tile_mode_to_wrap_mode(fTileModeY)};
206 
207     // Must set wrap and filter on the sampler before requesting a texture. In two places below
208     // we check the matrix scale factors to determine how to interpret the filter quality setting.
209     // This completely ignores the complexity of the drawVertices case where explicit local coords
210     // are provided by the caller.
211     bool doBicubic;
212     GrSamplerState::Filter textureFilterMode = GrSkFilterQualityToGrFilterMode(
213             fImage->width(), fImage->height(), args.fFilterQuality, *args.fViewMatrix, *lm,
214             args.fContext->priv().options().fSharpenMipmappedTextures, &doBicubic);
215     GrSamplerState samplerState(wm, textureFilterMode);
216     SkScalar scaleAdjust[2] = { 1.0f, 1.0f };
217     GrSurfaceProxyView view = as_IB(fImage)->refView(args.fContext, samplerState, scaleAdjust);
218     if (!view) {
219         return nullptr;
220     }
221 
222     SkAlphaType srcAlphaType = fImage->alphaType();
223 
224     lmInverse.postScale(scaleAdjust[0], scaleAdjust[1]);
225 
226     const auto& caps = *args.fContext->priv().caps();
227 
228     std::unique_ptr<GrFragmentProcessor> inner;
229     if (doBicubic) {
230         static constexpr auto kDir = GrBicubicEffect::Direction::kXY;
231         inner = GrBicubicEffect::Make(std::move(view), srcAlphaType, lmInverse, wm[0], wm[1], kDir,
232                                       caps);
233     } else {
234         inner = GrTextureEffect::Make(std::move(view), srcAlphaType, lmInverse, samplerState, caps);
235     }
236     inner = GrColorSpaceXformEffect::Make(std::move(inner), fImage->colorSpace(), srcAlphaType,
237                                           args.fDstColorInfo->colorSpace());
238 
239     bool isAlphaOnly = SkColorTypeIsAlphaOnly(fImage->colorType());
240     if (isAlphaOnly) {
241         return inner;
242     } else if (args.fInputColorIsOpaque) {
243         return GrFragmentProcessor::OverrideInput(std::move(inner), SK_PMColor4fWHITE, false);
244     }
245     return GrFragmentProcessor::MulChildByInputAlpha(std::move(inner));
246 }
247 
248 #endif
249 
250 ///////////////////////////////////////////////////////////////////////////////////////////////////
251 #include "src/core/SkImagePriv.h"
252 
SkMakeBitmapShader(const SkBitmap & src,SkTileMode tmx,SkTileMode tmy,const SkMatrix * localMatrix,SkCopyPixelsMode cpm)253 sk_sp<SkShader> SkMakeBitmapShader(const SkBitmap& src, SkTileMode tmx, SkTileMode tmy,
254                                    const SkMatrix* localMatrix, SkCopyPixelsMode cpm) {
255     return SkImageShader::Make(SkMakeImageFromRasterBitmap(src, cpm),
256                                tmx, tmy, localMatrix);
257 }
258 
SkMakeBitmapShaderForPaint(const SkPaint & paint,const SkBitmap & src,SkTileMode tmx,SkTileMode tmy,const SkMatrix * localMatrix,SkCopyPixelsMode mode)259 sk_sp<SkShader> SkMakeBitmapShaderForPaint(const SkPaint& paint, const SkBitmap& src,
260                                            SkTileMode tmx, SkTileMode tmy,
261                                            const SkMatrix* localMatrix, SkCopyPixelsMode mode) {
262     auto s = SkMakeBitmapShader(src, tmx, tmy, localMatrix, mode);
263     if (!s) {
264         return nullptr;
265     }
266     if (src.colorType() == kAlpha_8_SkColorType && paint.getShader()) {
267         // Compose the image shader with the paint's shader. Alpha images+shaders should output the
268         // texture's alpha multiplied by the shader's color. DstIn (d*sa) will achieve this with
269         // the source image and dst shader (MakeBlend takes dst first, src second).
270         s = SkShaders::Blend(SkBlendMode::kDstIn, paint.refShader(), std::move(s));
271     }
272     return s;
273 }
274 
RegisterFlattenables()275 void SkShaderBase::RegisterFlattenables() { SK_REGISTER_FLATTENABLE(SkImageShader); }
276 
277 class SkImageStageUpdater : public SkStageUpdater {
278 public:
SkImageStageUpdater(const SkImageShader * shader,bool usePersp)279     SkImageStageUpdater(const SkImageShader* shader, bool usePersp)
280         : fShader(shader), fUsePersp(usePersp)
281     {}
282 
283     const SkImageShader* fShader;
284     const bool           fUsePersp; // else use affine
285 
286     // large enough for perspective, though often we just use 2x3
287     float fMatrixStorage[9];
288 
289 #if 0   // TODO: when we support mipmaps
290     SkRasterPipeline_GatherCtx* fGather;
291     SkRasterPipeline_TileCtx* fLimitX;
292     SkRasterPipeline_TileCtx* fLimitY;
293     SkRasterPipeline_DecalTileCtx* fDecal;
294 #endif
295 
append_matrix_stage(SkRasterPipeline * p)296     void append_matrix_stage(SkRasterPipeline* p) {
297         if (fUsePersp) {
298             p->append(SkRasterPipeline::matrix_perspective, fMatrixStorage);
299         } else {
300             p->append(SkRasterPipeline::matrix_2x3, fMatrixStorage);
301         }
302     }
303 
update(const SkMatrix & ctm,const SkMatrix * localM)304     bool update(const SkMatrix& ctm, const SkMatrix* localM) override {
305         SkMatrix matrix;
306         if (fShader->computeTotalInverse(ctm, localM, &matrix)) {
307             if (fUsePersp) {
308                 matrix.get9(fMatrixStorage);
309             } else {
310                SkAssertResult(matrix.asAffine(fMatrixStorage));
311             }
312             return true;
313         }
314         return false;
315     }
316 };
317 
tweak_quality_and_inv_matrix(SkFilterQuality * quality,SkMatrix * matrix)318 static void tweak_quality_and_inv_matrix(SkFilterQuality* quality, SkMatrix* matrix) {
319     // When the matrix is just an integer translate, bilerp == nearest neighbor.
320     if (*quality == kLow_SkFilterQuality &&
321             matrix->getType() <= SkMatrix::kTranslate_Mask &&
322             matrix->getTranslateX() == (int)matrix->getTranslateX() &&
323             matrix->getTranslateY() == (int)matrix->getTranslateY()) {
324         *quality = kNone_SkFilterQuality;
325     }
326 
327     // See skia:4649 and the GM image_scale_aligned.
328     if (*quality == kNone_SkFilterQuality) {
329         if (matrix->getScaleX() >= 0) {
330             matrix->setTranslateX(nextafterf(matrix->getTranslateX(),
331                                              floorf(matrix->getTranslateX())));
332         }
333         if (matrix->getScaleY() >= 0) {
334             matrix->setTranslateY(nextafterf(matrix->getTranslateY(),
335                                              floorf(matrix->getTranslateY())));
336         }
337     }
338 }
339 
doStages(const SkStageRec & rec,SkImageStageUpdater * updater) const340 bool SkImageShader::doStages(const SkStageRec& rec, SkImageStageUpdater* updater) const {
341     if (updater && rec.fPaint.getFilterQuality() == kMedium_SkFilterQuality) {
342         // TODO: medium: recall RequestBitmap and update width/height accordingly
343         return false;
344     }
345 
346     SkRasterPipeline* p = rec.fPipeline;
347     SkArenaAlloc* alloc = rec.fAlloc;
348     auto quality = rec.fPaint.getFilterQuality();
349 
350     SkMatrix matrix;
351     if (!this->computeTotalInverse(rec.fCTM, rec.fLocalM, &matrix)) {
352         return false;
353     }
354 
355     const auto* state = SkBitmapController::RequestBitmap(as_IB(fImage.get()),
356                                                           matrix, quality, alloc);
357     if (!state) {
358         return false;
359     }
360 
361     const SkPixmap& pm = state->pixmap();
362     matrix  = state->invMatrix();
363     quality = state->quality();
364     auto info = pm.info();
365 
366     p->append(SkRasterPipeline::seed_shader);
367 
368     if (updater) {
369         updater->append_matrix_stage(p);
370     } else {
371         tweak_quality_and_inv_matrix(&quality, &matrix);
372         p->append_matrix(alloc, matrix);
373     }
374 
375     auto gather = alloc->make<SkRasterPipeline_GatherCtx>();
376     gather->pixels = pm.addr();
377     gather->stride = pm.rowBytesAsPixels();
378     gather->width  = pm.width();
379     gather->height = pm.height();
380 
381     auto limit_x = alloc->make<SkRasterPipeline_TileCtx>(),
382          limit_y = alloc->make<SkRasterPipeline_TileCtx>();
383     limit_x->scale = pm.width();
384     limit_x->invScale = 1.0f / pm.width();
385     limit_y->scale = pm.height();
386     limit_y->invScale = 1.0f / pm.height();
387 
388     SkRasterPipeline_DecalTileCtx* decal_ctx = nullptr;
389     bool decal_x_and_y = fTileModeX == SkTileMode::kDecal && fTileModeY == SkTileMode::kDecal;
390     if (fTileModeX == SkTileMode::kDecal || fTileModeY == SkTileMode::kDecal) {
391         decal_ctx = alloc->make<SkRasterPipeline_DecalTileCtx>();
392         decal_ctx->limit_x = limit_x->scale;
393         decal_ctx->limit_y = limit_y->scale;
394     }
395 
396 #if 0   // TODO: when we support kMedium
397     if (updator && (quality == kMedium_SkFilterQuality)) {
398         // if we change levels in mipmap, we need to update the scales (and invScales)
399         updator->fGather = gather;
400         updator->fLimitX = limit_x;
401         updator->fLimitY = limit_y;
402         updator->fDecal = decal_ctx;
403     }
404 #endif
405 
406     auto append_tiling_and_gather = [&] {
407         if (decal_x_and_y) {
408             p->append(SkRasterPipeline::decal_x_and_y,  decal_ctx);
409         } else {
410             switch (fTileModeX) {
411                 case SkTileMode::kClamp:  /* The gather_xxx stage will clamp for us. */     break;
412                 case SkTileMode::kMirror: p->append(SkRasterPipeline::mirror_x, limit_x);   break;
413                 case SkTileMode::kRepeat: p->append(SkRasterPipeline::repeat_x, limit_x);   break;
414                 case SkTileMode::kDecal:  p->append(SkRasterPipeline::decal_x,  decal_ctx); break;
415             }
416             switch (fTileModeY) {
417                 case SkTileMode::kClamp:  /* The gather_xxx stage will clamp for us. */     break;
418                 case SkTileMode::kMirror: p->append(SkRasterPipeline::mirror_y, limit_y);   break;
419                 case SkTileMode::kRepeat: p->append(SkRasterPipeline::repeat_y, limit_y);   break;
420                 case SkTileMode::kDecal:  p->append(SkRasterPipeline::decal_y,  decal_ctx); break;
421             }
422         }
423 
424         void* ctx = gather;
425         switch (info.colorType()) {
426             case kAlpha_8_SkColorType:      p->append(SkRasterPipeline::gather_a8,      ctx); break;
427             case kA16_unorm_SkColorType:    p->append(SkRasterPipeline::gather_a16,     ctx); break;
428             case kA16_float_SkColorType:    p->append(SkRasterPipeline::gather_af16,    ctx); break;
429             case kRGB_565_SkColorType:      p->append(SkRasterPipeline::gather_565,     ctx); break;
430             case kARGB_4444_SkColorType:    p->append(SkRasterPipeline::gather_4444,    ctx); break;
431             case kR8G8_unorm_SkColorType:   p->append(SkRasterPipeline::gather_rg88,    ctx); break;
432             case kR16G16_unorm_SkColorType: p->append(SkRasterPipeline::gather_rg1616,  ctx); break;
433             case kR16G16_float_SkColorType: p->append(SkRasterPipeline::gather_rgf16,  ctx);  break;
434             case kRGBA_8888_SkColorType:    p->append(SkRasterPipeline::gather_8888,    ctx); break;
435             case kRGBA_1010102_SkColorType: p->append(SkRasterPipeline::gather_1010102, ctx); break;
436             case kR16G16B16A16_unorm_SkColorType:
437                                             p->append(SkRasterPipeline::gather_16161616,ctx); break;
438             case kRGBA_F16Norm_SkColorType:
439             case kRGBA_F16_SkColorType:     p->append(SkRasterPipeline::gather_f16,     ctx); break;
440             case kRGBA_F32_SkColorType:     p->append(SkRasterPipeline::gather_f32,     ctx); break;
441 
442             case kGray_8_SkColorType:       p->append(SkRasterPipeline::gather_a8,      ctx);
443                                             p->append(SkRasterPipeline::alpha_to_gray      ); break;
444 
445             case kRGB_888x_SkColorType:     p->append(SkRasterPipeline::gather_8888,    ctx);
446                                             p->append(SkRasterPipeline::force_opaque       ); break;
447 
448             case kBGRA_1010102_SkColorType: p->append(SkRasterPipeline::gather_1010102, ctx);
449                                             p->append(SkRasterPipeline::swap_rb            ); break;
450 
451             case kRGB_101010x_SkColorType:  p->append(SkRasterPipeline::gather_1010102, ctx);
452                                             p->append(SkRasterPipeline::force_opaque       ); break;
453 
454             case kBGR_101010x_SkColorType:  p->append(SkRasterPipeline::gather_1010102, ctx);
455                                             p->append(SkRasterPipeline::force_opaque       );
456                                             p->append(SkRasterPipeline::swap_rb            ); break;
457 
458             case kBGRA_8888_SkColorType:    p->append(SkRasterPipeline::gather_8888,    ctx);
459                                             p->append(SkRasterPipeline::swap_rb            ); break;
460 
461             case kUnknown_SkColorType: SkASSERT(false);
462         }
463         if (decal_ctx) {
464             p->append(SkRasterPipeline::check_decal_mask, decal_ctx);
465         }
466     };
467 
468     auto append_misc = [&] {
469         // This is an inessential optimization... it's logically safe to set this to false.
470         // But if...
471         //      - we know the image is definitely normalized, and
472         //      - we're doing some color space conversion, and
473         //      - sRGB curves are involved,
474         // then we can use slightly faster math that doesn't work well outside [0,1].
475         bool src_is_normalized = SkColorTypeIsNormalized(info.colorType());
476 
477         SkColorSpace* cs = info.colorSpace();
478         SkAlphaType   at = info.alphaType();
479 
480         // Color for A8 images comes from the paint.  TODO: all alpha images?  none?
481         if (info.colorType() == kAlpha_8_SkColorType) {
482             SkColor4f rgb = rec.fPaint.getColor4f();
483             p->append_set_rgb(alloc, rgb);
484 
485             src_is_normalized = rgb.fitsInBytes();
486             cs = sk_srgb_singleton();
487             at = kUnpremul_SkAlphaType;
488         }
489 
490         // Bicubic filtering naturally produces out of range values on both sides of [0,1].
491         if (quality == kHigh_SkFilterQuality) {
492             p->append(SkRasterPipeline::clamp_0);
493             p->append(at == kUnpremul_SkAlphaType || fClampAsIfUnpremul
494                           ? SkRasterPipeline::clamp_1
495                           : SkRasterPipeline::clamp_a);
496             src_is_normalized = true;
497         }
498 
499         // Transform color space and alpha type to match shader convention (dst CS, premul alpha).
500         alloc->make<SkColorSpaceXformSteps>(cs, at,
501                                             rec.fDstCS, kPremul_SkAlphaType)
502             ->apply(p, src_is_normalized);
503 
504         return true;
505     };
506 
507     // Check for fast-path stages.
508     auto ct = info.colorType();
509     if (true
510         && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType)
511         && quality == kLow_SkFilterQuality
512         && fTileModeX == SkTileMode::kClamp && fTileModeY == SkTileMode::kClamp) {
513 
514         p->append(SkRasterPipeline::bilerp_clamp_8888, gather);
515         if (ct == kBGRA_8888_SkColorType) {
516             p->append(SkRasterPipeline::swap_rb);
517         }
518         return append_misc();
519     }
520     if (true
521         && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType) // TODO: all formats
522         && quality == kLow_SkFilterQuality
523         && fTileModeX != SkTileMode::kDecal // TODO decal too?
524         && fTileModeY != SkTileMode::kDecal) {
525 
526         auto ctx = alloc->make<SkRasterPipeline_SamplerCtx2>();
527         *(SkRasterPipeline_GatherCtx*)(ctx) = *gather;
528         ctx->ct = ct;
529         ctx->tileX = fTileModeX;
530         ctx->tileY = fTileModeY;
531         ctx->invWidth  = 1.0f / ctx->width;
532         ctx->invHeight = 1.0f / ctx->height;
533         p->append(SkRasterPipeline::bilinear, ctx);
534         return append_misc();
535     }
536     if (true
537         && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType)
538         && quality == kHigh_SkFilterQuality
539         && fTileModeX == SkTileMode::kClamp && fTileModeY == SkTileMode::kClamp) {
540 
541         p->append(SkRasterPipeline::bicubic_clamp_8888, gather);
542         if (ct == kBGRA_8888_SkColorType) {
543             p->append(SkRasterPipeline::swap_rb);
544         }
545         return append_misc();
546     }
547     if (true
548         && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType) // TODO: all formats
549         && quality == kHigh_SkFilterQuality
550         && fTileModeX != SkTileMode::kDecal // TODO decal too?
551         && fTileModeY != SkTileMode::kDecal) {
552 
553         auto ctx = alloc->make<SkRasterPipeline_SamplerCtx2>();
554         *(SkRasterPipeline_GatherCtx*)(ctx) = *gather;
555         ctx->ct = ct;
556         ctx->tileX = fTileModeX;
557         ctx->tileY = fTileModeY;
558         ctx->invWidth  = 1.0f / ctx->width;
559         ctx->invHeight = 1.0f / ctx->height;
560         p->append(SkRasterPipeline::bicubic, ctx);
561         return append_misc();
562     }
563 
564     SkRasterPipeline_SamplerCtx* sampler = nullptr;
565     if (quality != kNone_SkFilterQuality) {
566         sampler = alloc->make<SkRasterPipeline_SamplerCtx>();
567     }
568 
569     auto sample = [&](SkRasterPipeline::StockStage setup_x,
570                       SkRasterPipeline::StockStage setup_y) {
571         p->append(setup_x, sampler);
572         p->append(setup_y, sampler);
573         append_tiling_and_gather();
574         p->append(SkRasterPipeline::accumulate, sampler);
575     };
576 
577     if (quality == kNone_SkFilterQuality) {
578         append_tiling_and_gather();
579     } else if (quality == kLow_SkFilterQuality) {
580         p->append(SkRasterPipeline::save_xy, sampler);
581 
582         sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_ny);
583         sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_ny);
584         sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_py);
585         sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_py);
586 
587         p->append(SkRasterPipeline::move_dst_src);
588 
589     } else {
590         SkASSERT(quality == kHigh_SkFilterQuality);
591         p->append(SkRasterPipeline::save_xy, sampler);
592 
593         sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n3y);
594         sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n3y);
595         sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n3y);
596         sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n3y);
597 
598         sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n1y);
599         sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n1y);
600         sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n1y);
601         sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n1y);
602 
603         sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p1y);
604         sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p1y);
605         sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p1y);
606         sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p1y);
607 
608         sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p3y);
609         sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p3y);
610         sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p3y);
611         sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p3y);
612 
613         p->append(SkRasterPipeline::move_dst_src);
614     }
615 
616     return append_misc();
617 }
618 
onAppendStages(const SkStageRec & rec) const619 bool SkImageShader::onAppendStages(const SkStageRec& rec) const {
620     return this->doStages(rec, nullptr);
621 }
622 
onAppendUpdatableStages(const SkStageRec & rec) const623 SkStageUpdater* SkImageShader::onAppendUpdatableStages(const SkStageRec& rec) const {
624     bool usePersp = rec.fCTM.hasPerspective();
625     auto updater = rec.fAlloc->make<SkImageStageUpdater>(this, usePersp);
626     return this->doStages(rec, updater) ? updater : nullptr;
627 }
628 
onProgram(skvm::Builder * p,const SkMatrix & ctm,const SkMatrix * localM,SkFilterQuality quality,SkColorSpace * dstCS,skvm::Uniforms * uniforms,SkArenaAlloc * alloc,skvm::F32 x,skvm::F32 y,skvm::F32 * r,skvm::F32 * g,skvm::F32 * b,skvm::F32 * a) const629 bool SkImageShader::onProgram(skvm::Builder* p,
630                               const SkMatrix& ctm, const SkMatrix* localM,
631                               SkFilterQuality quality, SkColorSpace* dstCS,
632                               skvm::Uniforms* uniforms, SkArenaAlloc* alloc,
633                               skvm::F32 x, skvm::F32 y,
634                               skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) const {
635     SkMatrix inv;
636     if (!this->computeTotalInverse(ctm, localM, &inv)) {
637         return false;
638     }
639 
640     // We use RequestBitmap() to make sure our SkBitmapController::State lives in the alloc.
641     // This lets the SkVMBlitter hang on to this state and keep our image alive.
642     auto state = SkBitmapController::RequestBitmap(as_IB(fImage.get()), inv, quality, alloc);
643     if (!state) {
644         return false;
645     }
646     const SkPixmap& pm = state->pixmap();
647     inv     = state->invMatrix();
648     quality = state->quality();
649     tweak_quality_and_inv_matrix(&quality, &inv);
650     inv.normalizePerspective();
651 
652     // Apply matrix to convert dst coords to sample center coords.
653     SkShaderBase::ApplyMatrix(p, inv, &x,&y,uniforms);
654 
655     // Bail out if sample() can't yet handle our image's color type.
656     switch (pm.colorType()) {
657         default: return false;
658         case   kRGB_565_SkColorType:
659         case  kRGB_888x_SkColorType:
660         case kRGBA_8888_SkColorType:
661         case kBGRA_8888_SkColorType:
662         case kRGBA_1010102_SkColorType:
663         case kBGRA_1010102_SkColorType:
664         case  kRGB_101010x_SkColorType:
665         case  kBGR_101010x_SkColorType: break;
666     }
667 
668     // We can exploit image opacity to skip work unpacking alpha channels.
669     const bool input_is_opaque = SkAlphaTypeIsOpaque(pm.alphaType())
670                               || SkColorTypeIsAlwaysOpaque(pm.colorType());
671 
672     // Each call to sample() will try to rewrite the same uniforms over and over,
673     // so remember where we start and reset back there each time.  That way each
674     // sample() call uses the same uniform offsets.
675     const size_t uniforms_before_sample = uniforms->buf.size();
676 
677     auto sample = [&](skvm::F32 sx, skvm::F32 sy) -> skvm::Color {
678         uniforms->buf.resize(uniforms_before_sample);
679 
680         // repeat() and mirror() are written assuming they'll be followed by a [0,scale) clamp.
681         auto repeat = [&](skvm::F32 v, float scale) {
682             skvm::F32 S = p->uniformF(uniforms->pushF(     scale)),
683                       I = p->uniformF(uniforms->pushF(1.0f/scale));
684             // v - floor(v/scale)*scale
685             return p->sub(v, p->mul(p->floor(p->mul(v,I)), S));
686         };
687         auto mirror = [&](skvm::F32 v, float scale) {
688             skvm::F32 S  = p->uniformF(uniforms->pushF(     scale)),
689                       I2 = p->uniformF(uniforms->pushF(0.5f/scale));
690             // abs( (v-scale) - (2*scale)*floor((v-scale)*(0.5f/scale)) - scale )
691             //      {---A---}   {------------------B------------------}
692             skvm::F32 A = p->sub(v,S),
693                       B = p->mul(p->add(S,S), p->floor(p->mul(A,I2)));
694             return p->abs(p->sub(p->sub(A,B), S));
695         };
696         switch (fTileModeX) {
697             case SkTileMode::kDecal:  /* handled after gather */ break;
698             case SkTileMode::kClamp:  /*    we always clamp   */ break;
699             case SkTileMode::kRepeat: sx = repeat(sx, pm.width()); break;
700             case SkTileMode::kMirror: sx = mirror(sx, pm.width()); break;
701         }
702         switch (fTileModeY) {
703             case SkTileMode::kDecal:  /* handled after gather */  break;
704             case SkTileMode::kClamp:  /*    we always clamp   */  break;
705             case SkTileMode::kRepeat: sy = repeat(sy, pm.height()); break;
706             case SkTileMode::kMirror: sy = mirror(sy, pm.height()); break;
707         }
708 
709         // Always clamp sample coordinates to [0,width), [0,height), both for memory
710         // safety and to handle the clamps still needed by kClamp, kRepeat, and kMirror.
711         auto clamp = [&](skvm::F32 v, float limit) {
712             // Subtract an ulp so the upper clamp limit excludes limit itself.
713             int bits;
714             memcpy(&bits, &limit, 4);
715             return p->clamp(v, p->splat(0.0f), p->uniformF(uniforms->push(bits-1)));
716         };
717         skvm::F32 clamped_x = clamp(sx, pm. width()),
718                   clamped_y = clamp(sy, pm.height());
719 
720         // Load pixels from pm.addr()[(int)sx + (int)sy*stride].
721         skvm::Builder::Uniform img = uniforms->pushPtr(pm.addr());
722         skvm::I32 index = p->add(p->trunc(clamped_x),
723                           p->mul(p->trunc(clamped_y),
724                                  p->uniform32(uniforms->push(pm.rowBytesAsPixels()))));
725         skvm::Color c;
726         switch (pm.colorType()) {
727             default: SkUNREACHABLE;
728             case   kRGB_565_SkColorType: c = p->unpack_565 (p->gather16(img, index)); break;
729 
730             case  kRGB_888x_SkColorType: [[fallthrough]];
731             case kRGBA_8888_SkColorType: c = p->unpack_8888(p->gather32(img, index));
732                                          break;
733             case kBGRA_8888_SkColorType: c = p->unpack_8888(p->gather32(img, index));
734                                          std::swap(c.r, c.b);
735                                          break;
736 
737             case  kRGB_101010x_SkColorType: [[fallthrough]];
738             case kRGBA_1010102_SkColorType: c = p->unpack_1010102(p->gather32(img, index));
739                                             break;
740 
741             case  kBGR_101010x_SkColorType: [[fallthrough]];
742             case kBGRA_1010102_SkColorType: c = p->unpack_1010102(p->gather32(img, index));
743                                             std::swap(c.r, c.b);
744                                             break;
745         }
746         // If we know the image is opaque, jump right to alpha = 1.0f, skipping work to unpack it.
747         if (input_is_opaque) {
748             c.a = p->splat(1.0f);
749         }
750 
751         // Mask away any pixels that we tried to sample outside the bounds in kDecal.
752         if (fTileModeX == SkTileMode::kDecal || fTileModeY == SkTileMode::kDecal) {
753             skvm::I32 mask = p->splat(~0);
754             if (fTileModeX == SkTileMode::kDecal) { mask = p->bit_and(mask, p->eq(sx, clamped_x)); }
755             if (fTileModeY == SkTileMode::kDecal) { mask = p->bit_and(mask, p->eq(sy, clamped_y)); }
756             c.r = p->bit_cast(p->bit_and(mask, p->bit_cast(c.r)));
757             c.g = p->bit_cast(p->bit_and(mask, p->bit_cast(c.g)));
758             c.b = p->bit_cast(p->bit_and(mask, p->bit_cast(c.b)));
759             c.a = p->bit_cast(p->bit_and(mask, p->bit_cast(c.a)));
760             // Notice that even if input_is_opaque, c.a might now be 0.
761         }
762 
763         return c;
764     };
765 
766     if (quality == kNone_SkFilterQuality) {
767         skvm::Color c = sample(x,y);
768         *r = c.r;
769         *g = c.g;
770         *b = c.b;
771         *a = c.a;
772     } else if (quality == kLow_SkFilterQuality) {
773         // Our four sample points are the corners of a logical 1x1 pixel
774         // box surrounding (x,y) at (0.5,0.5) off-center.
775         skvm::F32 left   = p->sub(x, p->splat(0.5f)),
776                   top    = p->sub(y, p->splat(0.5f)),
777                   right  = p->add(x, p->splat(0.5f)),
778                   bottom = p->add(y, p->splat(0.5f));
779 
780         // The fractional parts of right and bottom are our lerp factors in x and y respectively.
781         skvm::F32 fx = p->fract(right ),
782                   fy = p->fract(bottom);
783 
784         skvm::Color c = p->lerp(p->lerp(sample(left,top   ), sample(right,top   ), fx),
785                                 p->lerp(sample(left,bottom), sample(right,bottom), fx), fy);
786         *r = c.r;
787         *g = c.g;
788         *b = c.b;
789         *a = c.a;
790     } else {
791         SkASSERT(quality == kHigh_SkFilterQuality);
792 
793         // All bicubic samples have the same fractional offset (fx,fy) from the center.
794         // They're either the 16 corners of a 3x3 grid/ surrounding (x,y) at (0.5,0.5) off-center.
795         skvm::F32 fx = p->fract(p->add(x, p->splat(0.5f))),
796                   fy = p->fract(p->add(y, p->splat(0.5f)));
797 
798         // See GrCubicEffect for details of these weights.
799         // TODO: these maybe don't seem right looking at gm/bicubic and GrBicubicEffect.
800         auto near = [&](skvm::F32 t) {
801             // 1/18 + 9/18t + 27/18t^2 - 21/18t^3 == t ( t ( -21/18t + 27/18) + 9/18) + 1/18
802             return p->mad(t,
803                    p->mad(t,
804                    p->mad(t, p->splat(-21/18.0f),
805                              p->splat( 27/18.0f)),
806                              p->splat(  9/18.0f)),
807                              p->splat(  1/18.0f));
808         };
809         auto far = [&](skvm::F32 t) {
810             // 0/18 + 0/18*t - 6/18t^2 + 7/18t^3 == t^2 (7/18t - 6/18)
811             return p->mul(p->mul(t,t), p->mad(t, p->splat( 7/18.0f),
812                                                  p->splat(-6/18.0f)));
813         };
814         const skvm::F32 wx[] =  {
815             far (p->sub(p->splat(1.0f), fx)),
816             near(p->sub(p->splat(1.0f), fx)),
817             near(                       fx ),
818             far (                       fx ),
819         };
820         const skvm::F32 wy[] = {
821             far (p->sub(p->splat(1.0f), fy)),
822             near(p->sub(p->splat(1.0f), fy)),
823             near(                       fy ),
824             far (                       fy ),
825         };
826 
827         *r = *g = *b = *a = p->splat(0.0f);
828 
829         skvm::F32 sy = p->add(y, p->splat(-1.5f));
830         for (int j = 0; j < 4; j++, sy = p->add(sy, p->splat(1.0f))) {
831             skvm::F32 sx = p->add(x, p->splat(-1.5f));
832             for (int i = 0; i < 4; i++, sx = p->add(sx, p->splat(1.0f))) {
833                 skvm::Color c = sample(sx,sy);
834                 skvm::F32 w = p->mul(wx[i], wy[j]);
835 
836                 *r = p->mad(c.r,w, *r);
837                 *g = p->mad(c.g,w, *g);
838                 *b = p->mad(c.b,w, *b);
839                 *a = p->mad(c.a,w, *a);
840             }
841         }
842     }
843 
844     // If the input is opaque and we're not in decal mode, that means the output is too.
845     // Forcing *a to 1.0 here will retroactively skip any work we did to interpolate sample alphas.
846     if (input_is_opaque
847             && fTileModeX != SkTileMode::kDecal
848             && fTileModeY != SkTileMode::kDecal) {
849         *a = p->splat(1.0f);
850     }
851 
852     if (quality == kHigh_SkFilterQuality) {
853         // Bicubic filtering naturally produces out of range values on both sides of [0,1].
854         *a = p->clamp(*a, p->splat(0.0f), p->splat(1.0f));
855 
856         skvm::F32 limit = (pm.alphaType() == kUnpremul_SkAlphaType || fClampAsIfUnpremul)
857                         ? p->splat(1.0f)
858                         : *a;
859         *r = p->clamp(*r, p->splat(0.0f), limit);
860         *g = p->clamp(*g, p->splat(0.0f), limit);
861         *b = p->clamp(*b, p->splat(0.0f), limit);
862     }
863 
864     // Follow SkColorSpaceXformSteps to match shader output convention (dstCS, premul).
865     // TODO: may need to extend lifetime once doing actual transforms?  maybe all in uniforms.
866     auto flags = SkColorSpaceXformSteps{pm.colorSpace(), pm.alphaType(),
867                                         dstCS, kPremul_SkAlphaType}.flags;
868 
869     // TODO: once this all works, move it to SkColorSpaceXformSteps
870     if (flags.unpremul)        { p->unpremul(r,g,b,*a); }
871     if (flags.linearize)       { return false; }
872     if (flags.gamut_transform) { return false; }
873     if (flags.encode)          { return false; }
874     if (flags.premul)          { p->premul(r,g,b,*a); }
875     return true;
876 }
877 
878