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