xref: /aosp_15_r20/external/skia/src/shaders/SkImageShader.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1  /*
2   * Copyright 2015 Google Inc.
3   *
4   * Use of this source code is governed by a BSD-style license that can be
5   * found in the LICENSE file.
6   */
7  
8  #include "src/shaders/SkImageShader.h"
9  
10  #include "include/core/SkAlphaType.h"
11  #include "include/core/SkBitmap.h"
12  #include "include/core/SkBlendMode.h"
13  #include "include/core/SkColorType.h"
14  #include "include/core/SkMatrix.h"
15  #include "include/core/SkPaint.h"
16  #include "include/core/SkPixmap.h"
17  #include "include/core/SkScalar.h"
18  #include "include/core/SkShader.h"
19  #include "include/core/SkTileMode.h"
20  #include "include/private/base/SkMath.h"
21  #include "modules/skcms/skcms.h"
22  #include "src/base/SkArenaAlloc.h"
23  #include "src/core/SkBitmapProcState.h"
24  #include "src/core/SkColorSpaceXformSteps.h"
25  #include "src/core/SkEffectPriv.h"
26  #include "src/core/SkImageInfoPriv.h"
27  #include "src/core/SkImagePriv.h"
28  #include "src/core/SkMipmapAccessor.h"
29  #include "src/core/SkPicturePriv.h"
30  #include "src/core/SkRasterPipeline.h"
31  #include "src/core/SkRasterPipelineOpContexts.h"
32  #include "src/core/SkRasterPipelineOpList.h"
33  #include "src/core/SkReadBuffer.h"
34  #include "src/core/SkSamplingPriv.h"
35  #include "src/core/SkWriteBuffer.h"
36  #include "src/image/SkImage_Base.h"
37  
38  #ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
39  #include "src/shaders/SkBitmapProcShader.h"
40  #endif
41  
42  #include <optional>
43  #include <tuple>
44  #include <utility>
45  
46  class SkColorSpace;
47  
CubicResamplerMatrix(float B,float C)48  SkM44 SkImageShader::CubicResamplerMatrix(float B, float C) {
49  #if 0
50      constexpr SkM44 kMitchell = SkM44( 1.f/18.f, -9.f/18.f,  15.f/18.f,  -7.f/18.f,
51                                        16.f/18.f,  0.f/18.f, -36.f/18.f,  21.f/18.f,
52                                         1.f/18.f,  9.f/18.f,  27.f/18.f, -21.f/18.f,
53                                         0.f/18.f,  0.f/18.f,  -6.f/18.f,   7.f/18.f);
54  
55      constexpr SkM44 kCatmull = SkM44(0.0f, -0.5f,  1.0f, -0.5f,
56                                       1.0f,  0.0f, -2.5f,  1.5f,
57                                       0.0f,  0.5f,  2.0f, -1.5f,
58                                       0.0f,  0.0f, -0.5f,  0.5f);
59  
60      if (B == 1.0f/3 && C == 1.0f/3) {
61          return kMitchell;
62      }
63      if (B == 0 && C == 0.5f) {
64          return kCatmull;
65      }
66  #endif
67      return SkM44(    (1.f/6)*B, -(3.f/6)*B - C,       (3.f/6)*B + 2*C,    - (1.f/6)*B - C,
68                   1 - (2.f/6)*B,              0, -3 + (12.f/6)*B +   C,  2 - (9.f/6)*B - C,
69                       (1.f/6)*B,  (3.f/6)*B + C,  3 - (15.f/6)*B - 2*C, -2 + (9.f/6)*B + C,
70                               0,              0,                    -C,      (1.f/6)*B + C);
71  }
72  
73  /**
74   *  We are faster in clamp, so always use that tiling when we can.
75   */
optimize(SkTileMode tm,int dimension)76  static SkTileMode optimize(SkTileMode tm, int dimension) {
77      SkASSERT(dimension > 0);
78  #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
79      // need to update frameworks/base/libs/hwui/tests/unit/SkiaBehaviorTests.cpp:55 to allow
80      // for transforming to clamp.
81      return tm;
82  #else
83      // mirror and repeat on a 1px axis are the same as clamping, but decal will still transition to
84      // transparent black.
85      return (tm != SkTileMode::kDecal && dimension == 1) ? SkTileMode::kClamp : tm;
86  #endif
87  }
88  
89  #if defined(SK_DEBUG)
needs_subset(SkImage * img,const SkRect & subset)90  static bool needs_subset(SkImage* img, const SkRect& subset) {
91      return subset != SkRect::Make(img->dimensions());
92  }
93  #endif
94  
SkImageShader(sk_sp<SkImage> img,const SkRect & subset,SkTileMode tmx,SkTileMode tmy,const SkSamplingOptions & sampling,bool raw,bool clampAsIfUnpremul)95  SkImageShader::SkImageShader(sk_sp<SkImage> img,
96                               const SkRect& subset,
97                               SkTileMode tmx, SkTileMode tmy,
98                               const SkSamplingOptions& sampling,
99                               bool raw,
100                               bool clampAsIfUnpremul)
101          : fImage(std::move(img))
102          , fSampling(sampling)
103          , fTileModeX(optimize(tmx, fImage->width()))
104          , fTileModeY(optimize(tmy, fImage->height()))
105          , fSubset(subset)
106          , fRaw(raw)
107          , fClampAsIfUnpremul(clampAsIfUnpremul) {
108      // These options should never appear together:
109      SkASSERT(!fRaw || !fClampAsIfUnpremul);
110  
111      // Bicubic filtering of raw image shaders would add a surprising clamp - so we don't support it
112      SkASSERT(!fRaw || !fSampling.useCubic);
113  }
114  
115  // just used for legacy-unflattening
116  enum class LegacyFilterEnum {
117      kNone,
118      kLow,
119      kMedium,
120      kHigh,
121      // this is the special value for backward compatibility
122      kInheritFromPaint,
123      // this signals we should use the new SkFilterOptions
124      kUseFilterOptions,
125      // use cubic and ignore FilterOptions
126      kUseCubicResampler,
127  
128      kLast = kUseCubicResampler,
129  };
130  
131  // fClampAsIfUnpremul is always false when constructed through public APIs,
132  // so there's no need to read or write it here.
133  
CreateProc(SkReadBuffer & buffer)134  sk_sp<SkFlattenable> SkImageShader::CreateProc(SkReadBuffer& buffer) {
135      auto tmx = buffer.read32LE<SkTileMode>(SkTileMode::kLastTileMode);
136      auto tmy = buffer.read32LE<SkTileMode>(SkTileMode::kLastTileMode);
137  
138      SkSamplingOptions sampling;
139      bool readSampling = true;
140      if (buffer.isVersionLT(SkPicturePriv::kNoFilterQualityShaders_Version) &&
141          !buffer.readBool() /* legacy has_sampling */)
142      {
143          readSampling = false;
144          // we just default to Nearest in sampling
145      }
146      if (readSampling) {
147          sampling = buffer.readSampling();
148      }
149  
150      SkMatrix localMatrix;
151      if (buffer.isVersionLT(SkPicturePriv::Version::kNoShaderLocalMatrix)) {
152          buffer.readMatrix(&localMatrix);
153      }
154      sk_sp<SkImage> img = buffer.readImage();
155      if (!img) {
156          return nullptr;
157      }
158  
159      bool raw = buffer.isVersionLT(SkPicturePriv::Version::kRawImageShaders) ? false
160                                                                              : buffer.readBool();
161  
162      // TODO(skbug.com/12784): Subset is not serialized yet; it's only used by special images so it
163      // will never be written to an SKP.
164  
165      return raw ? SkImageShader::MakeRaw(std::move(img), tmx, tmy, sampling, &localMatrix)
166                 : SkImageShader::Make(std::move(img), tmx, tmy, sampling, &localMatrix);
167  }
168  
flatten(SkWriteBuffer & buffer) const169  void SkImageShader::flatten(SkWriteBuffer& buffer) const {
170      buffer.writeUInt((unsigned)fTileModeX);
171      buffer.writeUInt((unsigned)fTileModeY);
172  
173      buffer.writeSampling(fSampling);
174  
175      buffer.writeImage(fImage.get());
176      SkASSERT(fClampAsIfUnpremul == false);
177  
178      // TODO(skbug.com/12784): Subset is not serialized yet; it's only used by special images so it
179      // will never be written to an SKP.
180      SkASSERT(!needs_subset(fImage.get(), fSubset));
181  
182      buffer.writeBool(fRaw);
183  }
184  
isOpaque() const185  bool SkImageShader::isOpaque() const {
186      return fImage->isOpaque() &&
187             fTileModeX != SkTileMode::kDecal && fTileModeY != SkTileMode::kDecal;
188  }
189  
190  #ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
191  
legacy_shader_can_handle(const SkMatrix & inv)192  static bool legacy_shader_can_handle(const SkMatrix& inv) {
193      SkASSERT(!inv.hasPerspective());
194  
195      // Scale+translate methods are always present, but affine might not be.
196      if (!SkOpts::S32_alpha_D32_filter_DXDY && !inv.isScaleTranslate()) {
197          return false;
198      }
199  
200      // legacy code uses SkFixed 32.32, so ensure the inverse doesn't map device coordinates
201      // out of range.
202      const SkScalar max_dev_coord = 32767.0f;
203      const SkRect src = inv.mapRect(SkRect::MakeWH(max_dev_coord, max_dev_coord));
204  
205      // take 1/4 of max signed 32bits so we have room to subtract local values
206      const SkScalar max_fixed32dot32 = float(SK_MaxS32) * 0.25f;
207      if (!SkRect::MakeLTRB(-max_fixed32dot32, -max_fixed32dot32,
208                            +max_fixed32dot32, +max_fixed32dot32).contains(src)) {
209          return false;
210      }
211  
212      // legacy shader impl should be able to handle these matrices
213      return true;
214  }
215  
onMakeContext(const ContextRec & rec,SkArenaAlloc * alloc) const216  SkShaderBase::Context* SkImageShader::onMakeContext(const ContextRec& rec,
217                                                      SkArenaAlloc* alloc) const {
218      SkASSERT(!needs_subset(fImage.get(), fSubset)); // TODO(skbug.com/12784)
219      if (fImage->alphaType() == kUnpremul_SkAlphaType) {
220          return nullptr;
221      }
222      if (fImage->colorType() != kN32_SkColorType) {
223          return nullptr;
224      }
225      if (fTileModeX != fTileModeY) {
226          return nullptr;
227      }
228      if (fTileModeX == SkTileMode::kDecal || fTileModeY == SkTileMode::kDecal) {
229          return nullptr;
230      }
231  
232      SkSamplingOptions sampling = fSampling;
233      if (sampling.isAniso()) {
234          sampling = SkSamplingPriv::AnisoFallback(fImage->hasMipmaps());
235      }
236  
237      auto supported = [](const SkSamplingOptions& sampling) {
238          const std::tuple<SkFilterMode,SkMipmapMode> supported[] = {
239              {SkFilterMode::kNearest, SkMipmapMode::kNone},    // legacy None
240              {SkFilterMode::kLinear,  SkMipmapMode::kNone},    // legacy Low
241              {SkFilterMode::kLinear,  SkMipmapMode::kNearest}, // legacy Medium
242          };
243          for (auto [f, m] : supported) {
244              if (sampling.filter == f && sampling.mipmap == m) {
245                  return true;
246              }
247          }
248          return false;
249      };
250      if (sampling.useCubic || !supported(sampling)) {
251          return nullptr;
252      }
253  
254      // SkBitmapProcShader stores bitmap coordinates in a 16bit buffer,
255      // so it can't handle bitmaps larger than 65535.
256      //
257      // We back off another bit to 32767 to make small amounts of
258      // intermediate math safe, e.g. in
259      //
260      //     SkFixed fx = ...;
261      //     fx = tile(fx + SK_Fixed1);
262      //
263      // we want to make sure (fx + SK_Fixed1) never overflows.
264      if (fImage-> width() > 32767 ||
265          fImage->height() > 32767) {
266          return nullptr;
267      }
268  
269      SkMatrix inv;
270      if (!rec.fMatrixRec.totalInverse(&inv) || !legacy_shader_can_handle(inv)) {
271          return nullptr;
272      }
273  
274      if (!rec.isLegacyCompatible(fImage->colorSpace())) {
275          return nullptr;
276      }
277  
278      return SkBitmapProcLegacyShader::MakeContext(*this, fTileModeX, fTileModeY, sampling,
279                                                   as_IB(fImage.get()), rec, alloc);
280  }
281  #endif
282  
onIsAImage(SkMatrix * texM,SkTileMode xy[]) const283  SkImage* SkImageShader::onIsAImage(SkMatrix* texM, SkTileMode xy[]) const {
284      if (texM) {
285          *texM = SkMatrix::I();
286      }
287      if (xy) {
288          xy[0] = fTileModeX;
289          xy[1] = fTileModeY;
290      }
291      return const_cast<SkImage*>(fImage.get());
292  }
293  
Make(sk_sp<SkImage> image,SkTileMode tmx,SkTileMode tmy,const SkSamplingOptions & options,const SkMatrix * localMatrix,bool clampAsIfUnpremul)294  sk_sp<SkShader> SkImageShader::Make(sk_sp<SkImage> image,
295                                      SkTileMode tmx, SkTileMode tmy,
296                                      const SkSamplingOptions& options,
297                                      const SkMatrix* localMatrix,
298                                      bool clampAsIfUnpremul) {
299      SkRect subset = image ? SkRect::Make(image->dimensions()) : SkRect::MakeEmpty();
300      return MakeSubset(std::move(image), subset, tmx, tmy, options, localMatrix, clampAsIfUnpremul);
301  }
302  
MakeRaw(sk_sp<SkImage> image,SkTileMode tmx,SkTileMode tmy,const SkSamplingOptions & options,const SkMatrix * localMatrix)303  sk_sp<SkShader> SkImageShader::MakeRaw(sk_sp<SkImage> image,
304                                         SkTileMode tmx, SkTileMode tmy,
305                                         const SkSamplingOptions& options,
306                                         const SkMatrix* localMatrix) {
307      if (options.useCubic) {
308          return nullptr;
309      }
310      if (!image) {
311          return SkShaders::Empty();
312      }
313      auto subset = SkRect::Make(image->dimensions());
314  
315      sk_sp<SkShader> s = sk_make_sp<SkImageShader>(image,
316                                                    subset,
317                                                    tmx, tmy,
318                                                    options,
319                                                    /*raw=*/true,
320                                                    /*clampAsIfUnpremul=*/false);
321      return s->makeWithLocalMatrix(localMatrix ? *localMatrix : SkMatrix::I());
322  }
323  
MakeSubset(sk_sp<SkImage> image,const SkRect & subset,SkTileMode tmx,SkTileMode tmy,const SkSamplingOptions & options,const SkMatrix * localMatrix,bool clampAsIfUnpremul)324  sk_sp<SkShader> SkImageShader::MakeSubset(sk_sp<SkImage> image,
325                                            const SkRect& subset,
326                                            SkTileMode tmx, SkTileMode tmy,
327                                            const SkSamplingOptions& options,
328                                            const SkMatrix* localMatrix,
329                                            bool clampAsIfUnpremul) {
330      auto is_unit = [](float x) {
331          return x >= 0 && x <= 1;
332      };
333      if (options.useCubic) {
334          if (!is_unit(options.cubic.B) || !is_unit(options.cubic.C)) {
335              return nullptr;
336          }
337      }
338      if (!image || subset.isEmpty()) {
339          return SkShaders::Empty();
340      }
341  
342      // Validate subset and check if we can drop it
343      if (!SkRect::Make(image->bounds()).contains(subset)) {
344          return nullptr;
345      }
346  
347      sk_sp<SkShader> s = sk_make_sp<SkImageShader>(std::move(image),
348                                                    subset,
349                                                    tmx, tmy,
350                                                    options,
351                                                    /*raw=*/false,
352                                                    clampAsIfUnpremul);
353      return s->makeWithLocalMatrix(localMatrix ? *localMatrix : SkMatrix::I());
354  }
355  
356  ///////////////////////////////////////////////////////////////////////////////////////////////////
357  
SkMakeBitmapShaderForPaint(const SkPaint & paint,const SkBitmap & src,SkTileMode tmx,SkTileMode tmy,const SkSamplingOptions & sampling,const SkMatrix * localMatrix,SkCopyPixelsMode mode)358  sk_sp<SkShader> SkMakeBitmapShaderForPaint(const SkPaint& paint, const SkBitmap& src,
359                                             SkTileMode tmx, SkTileMode tmy,
360                                             const SkSamplingOptions& sampling,
361                                             const SkMatrix* localMatrix, SkCopyPixelsMode mode) {
362      auto s = SkImageShader::Make(SkMakeImageFromRasterBitmap(src, mode),
363                                   tmx, tmy, sampling, localMatrix);
364      if (!s) {
365          return nullptr;
366      }
367      if (SkColorTypeIsAlphaOnly(src.colorType()) && paint.getShader()) {
368          // Compose the image shader with the paint's shader. Alpha images+shaders should output the
369          // texture's alpha multiplied by the shader's color. DstIn (d*sa) will achieve this with
370          // the source image and dst shader (MakeBlend takes dst first, src second).
371          s = SkShaders::Blend(SkBlendMode::kDstIn, paint.refShader(), std::move(s));
372      }
373      return s;
374  }
375  
SkModifyPaintAndDstForDrawImageRect(const SkImage * image,const SkSamplingOptions & sampling,SkRect src,SkRect dst,bool strictSrcSubset,SkPaint * paint)376  SkRect SkModifyPaintAndDstForDrawImageRect(const SkImage* image,
377                                             const SkSamplingOptions& sampling,
378                                             SkRect src,
379                                             SkRect dst,
380                                             bool strictSrcSubset,
381                                             SkPaint* paint) {
382      // The paint should have already been cleaned for a regular drawImageRect, e.g. no path
383      // effect and is a fill.
384      SkASSERT(paint);
385      SkASSERT(paint->getStyle() == SkPaint::kFill_Style && !paint->getPathEffect());
386  
387      SkASSERT(image);
388      SkRect imgBounds = SkRect::Make(image->bounds());
389  
390      SkASSERT(src.isFinite() && dst.isFinite() && dst.isSorted());
391      SkMatrix localMatrix = SkMatrix::RectToRect(src, dst);
392      if (!imgBounds.contains(src)) {
393          if (!src.intersect(imgBounds)) {
394              return SkRect::MakeEmpty(); // Nothing to draw for this entry
395          }
396          // Update dst to match smaller src
397          dst = localMatrix.mapRect(src);
398      }
399  
400      bool imageIsAlphaOnly = SkColorTypeIsAlphaOnly(image->colorType());
401  
402      sk_sp<SkShader> imgShader;
403      if (strictSrcSubset) {
404          imgShader = SkImageShader::MakeSubset(sk_ref_sp(image), src,
405                                                SkTileMode::kClamp, SkTileMode::kClamp,
406                                                sampling, &localMatrix);
407      } else {
408          imgShader = image->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
409                                        sampling, &localMatrix);
410      }
411      if (!imgShader) {
412          return SkRect::MakeEmpty();
413      }
414      if (imageIsAlphaOnly && paint->getShader()) {
415          // Compose the image shader with the paint's shader. Alpha images+shaders should output the
416          // texture's alpha multiplied by the shader's color. DstIn (d*sa) will achieve this with
417          // the source image and dst shader (MakeBlend takes dst first, src second).
418          imgShader = SkShaders::Blend(SkBlendMode::kDstIn, paint->refShader(), std::move(imgShader));
419      }
420  
421      paint->setShader(std::move(imgShader));
422      return dst;
423  }
424  
RegisterFlattenables()425  void SkShaderBase::RegisterFlattenables() { SK_REGISTER_FLATTENABLE(SkImageShader); }
426  
427  namespace {
428  
429  struct MipLevelHelper {
430      SkPixmap pm;
431      SkMatrix inv;
432      SkRasterPipeline_GatherCtx* gather;
433      SkRasterPipeline_TileCtx* limitX;
434      SkRasterPipeline_TileCtx* limitY;
435      SkRasterPipeline_DecalTileCtx* decalCtx = nullptr;
436  
allocAndInit__anonc68227f70311::MipLevelHelper437      void allocAndInit(SkArenaAlloc* alloc,
438                        const SkSamplingOptions& sampling,
439                        SkTileMode tileModeX,
440                        SkTileMode tileModeY) {
441          gather = alloc->make<SkRasterPipeline_GatherCtx>();
442          gather->pixels = pm.addr();
443          gather->stride = pm.rowBytesAsPixels();
444          gather->width = pm.width();
445          gather->height = pm.height();
446  
447          if (sampling.useCubic) {
448              SkImageShader::CubicResamplerMatrix(sampling.cubic.B, sampling.cubic.C)
449                      .getColMajor(gather->weights);
450          }
451  
452          limitX = alloc->make<SkRasterPipeline_TileCtx>();
453          limitY = alloc->make<SkRasterPipeline_TileCtx>();
454          limitX->scale = pm.width();
455          limitX->invScale = 1.0f / pm.width();
456          limitY->scale = pm.height();
457          limitY->invScale = 1.0f / pm.height();
458  
459          // We would like an image that is mapped 1:1 with device pixels but at a half pixel offset
460          // to select every pixel from the src image once. Our rasterizer biases upward. That is a
461          // rect from 0.5...1.5 fills pixel 1 and not pixel 0. So we make exact integer pixel sample
462          // values select the pixel to the left/above the integer value.
463          //
464          // Note that a mirror mapping between canvas and image space will not have this property -
465          // on one side of the image a row/column will be skipped and one repeated on the other side.
466          //
467          // The GM nearest_half_pixel_image tests both of the above scenarios.
468          //
469          // The implementation of SkTileMode::kMirror also modifies integer pixel snapping to create
470          // consistency when the sample coords are running backwards and must account for gather
471          // modification we perform here. The GM mirror_tile tests this.
472          if (!sampling.useCubic && sampling.filter == SkFilterMode::kNearest) {
473              gather->roundDownAtInteger = true;
474              limitX->mirrorBiasDir = limitY->mirrorBiasDir = 1;
475          }
476  
477          if (tileModeX == SkTileMode::kDecal || tileModeY == SkTileMode::kDecal) {
478              decalCtx = alloc->make<SkRasterPipeline_DecalTileCtx>();
479              decalCtx->limit_x = limitX->scale;
480              decalCtx->limit_y = limitY->scale;
481  
482              // When integer sample coords snap left/up then we want the right/bottom edge of the
483              // image bounds to be inside the image rather than the left/top edge, that is (0, w]
484              // rather than [0, w).
485              if (gather->roundDownAtInteger) {
486                  decalCtx->inclusiveEdge_x = decalCtx->limit_x;
487                  decalCtx->inclusiveEdge_y = decalCtx->limit_y;
488              }
489          }
490      }
491  };
492  
493  }  // namespace
494  
tweak_sampling(SkSamplingOptions sampling,const SkMatrix & matrix)495  static SkSamplingOptions tweak_sampling(SkSamplingOptions sampling, const SkMatrix& matrix) {
496      SkFilterMode filter = sampling.filter;
497  
498      // When the matrix is just an integer translate, bilerp == nearest neighbor.
499      if (filter == SkFilterMode::kLinear &&
500              matrix.getType() <= SkMatrix::kTranslate_Mask &&
501              matrix.getTranslateX() == (int)matrix.getTranslateX() &&
502              matrix.getTranslateY() == (int)matrix.getTranslateY()) {
503          filter = SkFilterMode::kNearest;
504      }
505  
506      return SkSamplingOptions(filter, sampling.mipmap);
507  }
508  
appendStages(const SkStageRec & rec,const SkShaders::MatrixRec & mRec) const509  bool SkImageShader::appendStages(const SkStageRec& rec, const SkShaders::MatrixRec& mRec) const {
510      SkASSERT(!needs_subset(fImage.get(), fSubset));  // TODO(skbug.com/12784)
511  
512      // We only support certain sampling options in stages so far
513      auto sampling = fSampling;
514      if (sampling.isAniso()) {
515          sampling = SkSamplingPriv::AnisoFallback(fImage->hasMipmaps());
516      }
517  
518      SkRasterPipeline* p = rec.fPipeline;
519      SkArenaAlloc* alloc = rec.fAlloc;
520  
521      SkMatrix baseInv;
522      // If the total matrix isn't valid then we will always access the base MIP level.
523      if (mRec.totalMatrixIsValid()) {
524          if (!mRec.totalInverse(&baseInv)) {
525              return false;
526          }
527          baseInv.normalizePerspective();
528      }
529  
530      SkASSERT(!sampling.useCubic || sampling.mipmap == SkMipmapMode::kNone);
531      auto* access = SkMipmapAccessor::Make(alloc, fImage.get(), baseInv, sampling.mipmap);
532      if (!access) {
533          return false;
534      }
535  
536      MipLevelHelper upper;
537      std::tie(upper.pm, upper.inv) = access->level();
538  
539      if (!sampling.useCubic) {
540          // TODO: can tweak_sampling sometimes for cubic too when B=0
541          if (mRec.totalMatrixIsValid()) {
542              sampling = tweak_sampling(sampling, SkMatrix::Concat(upper.inv, baseInv));
543          }
544      }
545  
546      if (!mRec.apply(rec, upper.inv)) {
547          return false;
548      }
549  
550      upper.allocAndInit(alloc, sampling, fTileModeX, fTileModeY);
551  
552      MipLevelHelper lower;
553      SkRasterPipeline_MipmapCtx* mipmapCtx = nullptr;
554      float lowerWeight = access->lowerWeight();
555      if (lowerWeight > 0) {
556          std::tie(lower.pm, lower.inv) = access->lowerLevel();
557          mipmapCtx = alloc->make<SkRasterPipeline_MipmapCtx>();
558          mipmapCtx->lowerWeight = lowerWeight;
559          mipmapCtx->scaleX = static_cast<float>(lower.pm.width()) / upper.pm.width();
560          mipmapCtx->scaleY = static_cast<float>(lower.pm.height()) / upper.pm.height();
561  
562          lower.allocAndInit(alloc, sampling, fTileModeX, fTileModeY);
563  
564          p->append(SkRasterPipelineOp::mipmap_linear_init, mipmapCtx);
565      }
566  
567      const bool decalBothAxes = fTileModeX == SkTileMode::kDecal && fTileModeY == SkTileMode::kDecal;
568  
569      auto append_tiling_and_gather = [&](const MipLevelHelper* level) {
570          if (decalBothAxes) {
571              p->append(SkRasterPipelineOp::decal_x_and_y,  level->decalCtx);
572          } else {
573              switch (fTileModeX) {
574                  case SkTileMode::kClamp: /* The gather_xxx stage will clamp for us. */
575                      break;
576                  case SkTileMode::kMirror:
577                      p->append(SkRasterPipelineOp::mirror_x, level->limitX);
578                      break;
579                  case SkTileMode::kRepeat:
580                      p->append(SkRasterPipelineOp::repeat_x, level->limitX);
581                      break;
582                  case SkTileMode::kDecal:
583                      p->append(SkRasterPipelineOp::decal_x, level->decalCtx);
584                      break;
585              }
586              switch (fTileModeY) {
587                  case SkTileMode::kClamp: /* The gather_xxx stage will clamp for us. */
588                      break;
589                  case SkTileMode::kMirror:
590                      p->append(SkRasterPipelineOp::mirror_y, level->limitY);
591                      break;
592                  case SkTileMode::kRepeat:
593                      p->append(SkRasterPipelineOp::repeat_y, level->limitY);
594                      break;
595                  case SkTileMode::kDecal:
596                      p->append(SkRasterPipelineOp::decal_y, level->decalCtx);
597                      break;
598              }
599          }
600  
601          void* ctx = level->gather;
602          switch (level->pm.colorType()) {
603              case kAlpha_8_SkColorType:      p->append(SkRasterPipelineOp::gather_a8,    ctx); break;
604              case kA16_unorm_SkColorType:    p->append(SkRasterPipelineOp::gather_a16,   ctx); break;
605              case kA16_float_SkColorType:    p->append(SkRasterPipelineOp::gather_af16,  ctx); break;
606              case kRGB_565_SkColorType:      p->append(SkRasterPipelineOp::gather_565,   ctx); break;
607              case kARGB_4444_SkColorType:    p->append(SkRasterPipelineOp::gather_4444,  ctx); break;
608              case kR8G8_unorm_SkColorType:   p->append(SkRasterPipelineOp::gather_rg88,  ctx); break;
609              case kR16G16_unorm_SkColorType: p->append(SkRasterPipelineOp::gather_rg1616,ctx); break;
610              case kR16G16_float_SkColorType: p->append(SkRasterPipelineOp::gather_rgf16, ctx); break;
611              case kRGBA_8888_SkColorType:    p->append(SkRasterPipelineOp::gather_8888,  ctx); break;
612  
613              case kRGBA_1010102_SkColorType:
614                  p->append(SkRasterPipelineOp::gather_1010102, ctx);
615                  break;
616  
617              case kR16G16B16A16_unorm_SkColorType:
618                  p->append(SkRasterPipelineOp::gather_16161616, ctx);
619                  break;
620  
621              case kRGBA_F16Norm_SkColorType:
622              case kRGBA_F16_SkColorType:     p->append(SkRasterPipelineOp::gather_f16,   ctx); break;
623              case kRGBA_F32_SkColorType:     p->append(SkRasterPipelineOp::gather_f32,   ctx); break;
624              case kBGRA_10101010_XR_SkColorType:
625                  p->append(SkRasterPipelineOp::gather_10101010_xr,  ctx);
626                  p->append(SkRasterPipelineOp::swap_rb);
627                  break;
628              case kRGBA_10x6_SkColorType:    p->append(SkRasterPipelineOp::gather_10x6,  ctx); break;
629  
630              case kGray_8_SkColorType:       p->append(SkRasterPipelineOp::gather_a8,    ctx);
631                                              p->append(SkRasterPipelineOp::alpha_to_gray    ); break;
632  
633              case kR8_unorm_SkColorType:     p->append(SkRasterPipelineOp::gather_a8,    ctx);
634                                              p->append(SkRasterPipelineOp::alpha_to_red     ); break;
635  
636              case kRGB_888x_SkColorType:     p->append(SkRasterPipelineOp::gather_8888,  ctx);
637                                              p->append(SkRasterPipelineOp::force_opaque     ); break;
638              case kRGB_F16F16F16x_SkColorType:
639                  p->append(SkRasterPipelineOp::gather_f16,  ctx);
640                  p->append(SkRasterPipelineOp::force_opaque);
641                  break;
642              case kBGRA_1010102_SkColorType:
643                  p->append(SkRasterPipelineOp::gather_1010102, ctx);
644                  p->append(SkRasterPipelineOp::swap_rb);
645                  break;
646  
647              case kRGB_101010x_SkColorType:
648                  p->append(SkRasterPipelineOp::gather_1010102, ctx);
649                  p->append(SkRasterPipelineOp::force_opaque);
650                  break;
651  
652              case kBGR_101010x_XR_SkColorType:
653                  p->append(SkRasterPipelineOp::gather_1010102_xr, ctx);
654                  p->append(SkRasterPipelineOp::force_opaque);
655                  p->append(SkRasterPipelineOp::swap_rb);
656                  break;
657  
658              case kBGR_101010x_SkColorType:
659                  p->append(SkRasterPipelineOp::gather_1010102, ctx);
660                  p->append(SkRasterPipelineOp::force_opaque);
661                  p->append(SkRasterPipelineOp::swap_rb);
662                  break;
663  
664              case kBGRA_8888_SkColorType:
665                  p->append(SkRasterPipelineOp::gather_8888, ctx);
666                  p->append(SkRasterPipelineOp::swap_rb);
667                  break;
668  
669              case kSRGBA_8888_SkColorType:
670                  p->append(SkRasterPipelineOp::gather_8888, ctx);
671                  p->appendTransferFunction(*skcms_sRGB_TransferFunction());
672                  break;
673  
674              case kUnknown_SkColorType: SkASSERT(false);
675          }
676          if (level->decalCtx) {
677              p->append(SkRasterPipelineOp::check_decal_mask, level->decalCtx);
678          }
679      };
680  
681      auto append_misc = [&] {
682          SkColorSpace* cs = upper.pm.colorSpace();
683          SkAlphaType   at = upper.pm.alphaType();
684  
685          // Color for alpha-only images comes from the paint (already converted to dst color space).
686          // If we were sampled by a runtime effect, the paint color was replaced with transparent
687          // black, so this tinting is effectively suppressed. See also: RuntimeEffectRPCallbacks
688          if (SkColorTypeIsAlphaOnly(upper.pm.colorType()) && !fRaw) {
689              p->appendSetRGB(alloc, rec.fPaintColor);
690  
691              cs = rec.fDstCS;
692              at = kUnpremul_SkAlphaType;
693          }
694  
695          // Bicubic filtering naturally produces out of range values on both sides of [0,1].
696          if (sampling.useCubic) {
697              p->append(at == kUnpremul_SkAlphaType || fClampAsIfUnpremul
698                            ? SkRasterPipelineOp::clamp_01
699                            : SkRasterPipelineOp::clamp_gamut);
700          }
701  
702          // Transform color space and alpha type to match shader convention (dst CS, premul alpha).
703          if (!fRaw) {
704              alloc->make<SkColorSpaceXformSteps>(cs, at, rec.fDstCS, kPremul_SkAlphaType)->apply(p);
705          }
706  
707          return true;
708      };
709  
710      // Check for fast-path stages.
711      // TODO: Could we use the fast-path stages for each level when doing linear mipmap filtering?
712      SkColorType ct = upper.pm.colorType();
713      if (true
714          && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType)
715          && !sampling.useCubic && sampling.filter == SkFilterMode::kLinear
716          && sampling.mipmap != SkMipmapMode::kLinear
717          && fTileModeX == SkTileMode::kClamp && fTileModeY == SkTileMode::kClamp) {
718  
719          p->append(SkRasterPipelineOp::bilerp_clamp_8888, upper.gather);
720          if (ct == kBGRA_8888_SkColorType) {
721              p->append(SkRasterPipelineOp::swap_rb);
722          }
723          return append_misc();
724      }
725      if (true
726          && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType)
727          && sampling.useCubic
728          && fTileModeX == SkTileMode::kClamp && fTileModeY == SkTileMode::kClamp) {
729  
730          p->append(SkRasterPipelineOp::bicubic_clamp_8888, upper.gather);
731          if (ct == kBGRA_8888_SkColorType) {
732              p->append(SkRasterPipelineOp::swap_rb);
733          }
734          return append_misc();
735      }
736  
737      // This context can be shared by both levels when doing linear mipmap filtering
738      SkRasterPipeline_SamplerCtx* sampler = alloc->make<SkRasterPipeline_SamplerCtx>();
739  
740      auto sample = [&](SkRasterPipelineOp setup_x,
741                        SkRasterPipelineOp setup_y,
742                        const MipLevelHelper* level) {
743          p->append(setup_x, sampler);
744          p->append(setup_y, sampler);
745          append_tiling_and_gather(level);
746          p->append(SkRasterPipelineOp::accumulate, sampler);
747      };
748  
749      auto sample_level = [&](const MipLevelHelper* level) {
750          if (sampling.useCubic) {
751              CubicResamplerMatrix(sampling.cubic.B, sampling.cubic.C).getColMajor(sampler->weights);
752  
753              p->append(SkRasterPipelineOp::bicubic_setup, sampler);
754  
755              sample(SkRasterPipelineOp::bicubic_n3x, SkRasterPipelineOp::bicubic_n3y, level);
756              sample(SkRasterPipelineOp::bicubic_n1x, SkRasterPipelineOp::bicubic_n3y, level);
757              sample(SkRasterPipelineOp::bicubic_p1x, SkRasterPipelineOp::bicubic_n3y, level);
758              sample(SkRasterPipelineOp::bicubic_p3x, SkRasterPipelineOp::bicubic_n3y, level);
759  
760              sample(SkRasterPipelineOp::bicubic_n3x, SkRasterPipelineOp::bicubic_n1y, level);
761              sample(SkRasterPipelineOp::bicubic_n1x, SkRasterPipelineOp::bicubic_n1y, level);
762              sample(SkRasterPipelineOp::bicubic_p1x, SkRasterPipelineOp::bicubic_n1y, level);
763              sample(SkRasterPipelineOp::bicubic_p3x, SkRasterPipelineOp::bicubic_n1y, level);
764  
765              sample(SkRasterPipelineOp::bicubic_n3x, SkRasterPipelineOp::bicubic_p1y, level);
766              sample(SkRasterPipelineOp::bicubic_n1x, SkRasterPipelineOp::bicubic_p1y, level);
767              sample(SkRasterPipelineOp::bicubic_p1x, SkRasterPipelineOp::bicubic_p1y, level);
768              sample(SkRasterPipelineOp::bicubic_p3x, SkRasterPipelineOp::bicubic_p1y, level);
769  
770              sample(SkRasterPipelineOp::bicubic_n3x, SkRasterPipelineOp::bicubic_p3y, level);
771              sample(SkRasterPipelineOp::bicubic_n1x, SkRasterPipelineOp::bicubic_p3y, level);
772              sample(SkRasterPipelineOp::bicubic_p1x, SkRasterPipelineOp::bicubic_p3y, level);
773              sample(SkRasterPipelineOp::bicubic_p3x, SkRasterPipelineOp::bicubic_p3y, level);
774  
775              p->append(SkRasterPipelineOp::move_dst_src);
776          } else if (sampling.filter == SkFilterMode::kLinear) {
777              p->append(SkRasterPipelineOp::bilinear_setup, sampler);
778  
779              sample(SkRasterPipelineOp::bilinear_nx, SkRasterPipelineOp::bilinear_ny, level);
780              sample(SkRasterPipelineOp::bilinear_px, SkRasterPipelineOp::bilinear_ny, level);
781              sample(SkRasterPipelineOp::bilinear_nx, SkRasterPipelineOp::bilinear_py, level);
782              sample(SkRasterPipelineOp::bilinear_px, SkRasterPipelineOp::bilinear_py, level);
783  
784              p->append(SkRasterPipelineOp::move_dst_src);
785          } else {
786              append_tiling_and_gather(level);
787          }
788      };
789  
790      sample_level(&upper);
791  
792      if (mipmapCtx) {
793          p->append(SkRasterPipelineOp::mipmap_linear_update, mipmapCtx);
794          sample_level(&lower);
795          p->append(SkRasterPipelineOp::mipmap_linear_finish, mipmapCtx);
796      }
797  
798      return append_misc();
799  }
800  
801  namespace SkShaders {
802  
Image(sk_sp<SkImage> image,SkTileMode tmx,SkTileMode tmy,const SkSamplingOptions & options,const SkMatrix * localMatrix)803  sk_sp<SkShader> Image(sk_sp<SkImage> image,
804                        SkTileMode tmx, SkTileMode tmy,
805                        const SkSamplingOptions& options,
806                        const SkMatrix* localMatrix) {
807      return SkImageShader::Make(std::move(image), tmx, tmy, options, localMatrix);
808  }
809  
RawImage(sk_sp<SkImage> image,SkTileMode tmx,SkTileMode tmy,const SkSamplingOptions & options,const SkMatrix * localMatrix)810  sk_sp<SkShader> RawImage(sk_sp<SkImage> image,
811                           SkTileMode tmx, SkTileMode tmy,
812                           const SkSamplingOptions& options,
813                           const SkMatrix* localMatrix) {
814      return SkImageShader::MakeRaw(std::move(image), tmx, tmy, options, localMatrix);
815  }
816  
817  }  // namespace SkShaders
818