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