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 "src/core/SkArenaAlloc.h"
11 #include "src/core/SkColorSpacePriv.h"
12 #include "src/core/SkColorSpaceXformSteps.h"
13 #include "src/core/SkMatrixPriv.h"
14 #include "src/core/SkMatrixProvider.h"
15 #include "src/core/SkMipmapAccessor.h"
16 #include "src/core/SkOpts.h"
17 #include "src/core/SkRasterPipeline.h"
18 #include "src/core/SkReadBuffer.h"
19 #include "src/core/SkSamplingPriv.h"
20 #include "src/core/SkScopeExit.h"
21 #include "src/core/SkVM.h"
22 #include "src/core/SkWriteBuffer.h"
23 #include "src/image/SkImage_Base.h"
24 #include "src/shaders/SkBitmapProcShader.h"
25 #include "src/shaders/SkEmptyShader.h"
26 #include "src/shaders/SkTransformShader.h"
27
CubicResamplerMatrix(float B,float C)28 SkM44 SkImageShader::CubicResamplerMatrix(float B, float C) {
29 #if 0
30 constexpr SkM44 kMitchell = SkM44( 1.f/18.f, -9.f/18.f, 15.f/18.f, -7.f/18.f,
31 16.f/18.f, 0.f/18.f, -36.f/18.f, 21.f/18.f,
32 1.f/18.f, 9.f/18.f, 27.f/18.f, -21.f/18.f,
33 0.f/18.f, 0.f/18.f, -6.f/18.f, 7.f/18.f);
34
35 constexpr SkM44 kCatmull = SkM44(0.0f, -0.5f, 1.0f, -0.5f,
36 1.0f, 0.0f, -2.5f, 1.5f,
37 0.0f, 0.5f, 2.0f, -1.5f,
38 0.0f, 0.0f, -0.5f, 0.5f);
39
40 if (B == 1.0f/3 && C == 1.0f/3) {
41 return kMitchell;
42 }
43 if (B == 0 && C == 0.5f) {
44 return kCatmull;
45 }
46 #endif
47 return SkM44( (1.f/6)*B, -(3.f/6)*B - C, (3.f/6)*B + 2*C, - (1.f/6)*B - C,
48 1 - (2.f/6)*B, 0, -3 + (12.f/6)*B + C, 2 - (9.f/6)*B - C,
49 (1.f/6)*B, (3.f/6)*B + C, 3 - (15.f/6)*B - 2*C, -2 + (9.f/6)*B + C,
50 0, 0, -C, (1.f/6)*B + C);
51 }
52
53 /**
54 * We are faster in clamp, so always use that tiling when we can.
55 */
optimize(SkTileMode tm,int dimension)56 static SkTileMode optimize(SkTileMode tm, int dimension) {
57 SkASSERT(dimension > 0);
58 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
59 // need to update frameworks/base/libs/hwui/tests/unit/SkiaBehaviorTests.cpp:55 to allow
60 // for transforming to clamp.
61 return tm;
62 #else
63 // mirror and repeat on a 1px axis are the same as clamping, but decal will still transition to
64 // transparent black.
65 return (tm != SkTileMode::kDecal && dimension == 1) ? SkTileMode::kClamp : tm;
66 #endif
67 }
68
SkImageShader(sk_sp<SkImage> img,SkTileMode tmx,SkTileMode tmy,const SkSamplingOptions & sampling,const SkMatrix * localMatrix,bool clampAsIfUnpremul)69 SkImageShader::SkImageShader(sk_sp<SkImage> img,
70 SkTileMode tmx, SkTileMode tmy,
71 const SkSamplingOptions& sampling,
72 const SkMatrix* localMatrix,
73 bool clampAsIfUnpremul)
74 : INHERITED(localMatrix)
75 , fImage(std::move(img))
76 , fSampling(sampling)
77 , fTileModeX(optimize(tmx, fImage->width()))
78 , fTileModeY(optimize(tmy, fImage->height()))
79 , fClampAsIfUnpremul(clampAsIfUnpremul)
80 {}
81
82 // just used for legacy-unflattening
83 enum class LegacyFilterEnum {
84 kNone,
85 kLow,
86 kMedium,
87 kHigh,
88 // this is the special value for backward compatibility
89 kInheritFromPaint,
90 // this signals we should use the new SkFilterOptions
91 kUseFilterOptions,
92 // use cubic and ignore FilterOptions
93 kUseCubicResampler,
94
95 kLast = kUseCubicResampler,
96 };
97
98 // fClampAsIfUnpremul is always false when constructed through public APIs,
99 // so there's no need to read or write it here.
100
CreateProc(SkReadBuffer & buffer)101 sk_sp<SkFlattenable> SkImageShader::CreateProc(SkReadBuffer& buffer) {
102 auto tmx = buffer.read32LE<SkTileMode>(SkTileMode::kLastTileMode);
103 auto tmy = buffer.read32LE<SkTileMode>(SkTileMode::kLastTileMode);
104
105 SkSamplingOptions sampling;
106 bool readSampling = true;
107 if (buffer.isVersionLT(SkPicturePriv::kNoFilterQualityShaders_Version) &&
108 !buffer.readBool() /* legacy has_sampling */)
109 {
110 readSampling = false;
111 // we just default to Nearest in sampling
112 }
113 if (readSampling) {
114 sampling = SkSamplingPriv::Read(buffer);
115 }
116
117 SkMatrix localMatrix;
118 buffer.readMatrix(&localMatrix);
119 sk_sp<SkImage> img = buffer.readImage();
120 if (!img) {
121 return nullptr;
122 }
123
124 return SkImageShader::Make(std::move(img), tmx, tmy, sampling, &localMatrix);
125 }
126
flatten(SkWriteBuffer & buffer) const127 void SkImageShader::flatten(SkWriteBuffer& buffer) const {
128 buffer.writeUInt((unsigned)fTileModeX);
129 buffer.writeUInt((unsigned)fTileModeY);
130
131 SkSamplingPriv::Write(buffer, fSampling);
132
133 buffer.writeMatrix(this->getLocalMatrix());
134 buffer.writeImage(fImage.get());
135 SkASSERT(fClampAsIfUnpremul == false);
136 }
137
isOpaque() const138 bool SkImageShader::isOpaque() const {
139 return fImage->isOpaque() &&
140 fTileModeX != SkTileMode::kDecal && fTileModeY != SkTileMode::kDecal;
141 }
142
143 constexpr SkCubicResampler kDefaultCubicResampler{1.0f/3, 1.0f/3};
144
is_default_cubic_resampler(SkCubicResampler cubic)145 static bool is_default_cubic_resampler(SkCubicResampler cubic) {
146 return SkScalarNearlyEqual(cubic.B, kDefaultCubicResampler.B) &&
147 SkScalarNearlyEqual(cubic.C, kDefaultCubicResampler.C);
148 }
149
150 #ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
151
legacy_shader_can_handle(const SkMatrix & inv)152 static bool legacy_shader_can_handle(const SkMatrix& inv) {
153 SkASSERT(!inv.hasPerspective());
154
155 // Scale+translate methods are always present, but affine might not be.
156 if (!SkOpts::S32_alpha_D32_filter_DXDY && !inv.isScaleTranslate()) {
157 return false;
158 }
159
160 // legacy code uses SkFixed 32.32, so ensure the inverse doesn't map device coordinates
161 // out of range.
162 const SkScalar max_dev_coord = 32767.0f;
163 const SkRect src = inv.mapRect(SkRect::MakeWH(max_dev_coord, max_dev_coord));
164
165 // take 1/4 of max signed 32bits so we have room to subtract local values
166 const SkScalar max_fixed32dot32 = float(SK_MaxS32) * 0.25f;
167 if (!SkRect::MakeLTRB(-max_fixed32dot32, -max_fixed32dot32,
168 +max_fixed32dot32, +max_fixed32dot32).contains(src)) {
169 return false;
170 }
171
172 // legacy shader impl should be able to handle these matrices
173 return true;
174 }
175
onMakeContext(const ContextRec & rec,SkArenaAlloc * alloc) const176 SkShaderBase::Context* SkImageShader::onMakeContext(const ContextRec& rec,
177 SkArenaAlloc* alloc) const {
178 if (fImage->alphaType() == kUnpremul_SkAlphaType) {
179 return nullptr;
180 }
181 if (fImage->colorType() != kN32_SkColorType) {
182 return nullptr;
183 }
184 if (fTileModeX != fTileModeY) {
185 return nullptr;
186 }
187 if (fTileModeX == SkTileMode::kDecal || fTileModeY == SkTileMode::kDecal) {
188 return nullptr;
189 }
190
191 auto supported = [](const SkSamplingOptions& sampling) {
192 const std::tuple<SkFilterMode,SkMipmapMode> supported[] = {
193 {SkFilterMode::kNearest, SkMipmapMode::kNone}, // legacy None
194 {SkFilterMode::kLinear, SkMipmapMode::kNone}, // legacy Low
195 {SkFilterMode::kLinear, SkMipmapMode::kNearest}, // legacy Medium
196 };
197 for (auto [f, m] : supported) {
198 if (sampling.filter == f && sampling.mipmap == m) {
199 return true;
200 }
201 }
202 return false;
203 };
204 if (fSampling.useCubic || !supported(fSampling)) {
205 return nullptr;
206 }
207
208 // SkBitmapProcShader stores bitmap coordinates in a 16bit buffer,
209 // so it can't handle bitmaps larger than 65535.
210 //
211 // We back off another bit to 32767 to make small amounts of
212 // intermediate math safe, e.g. in
213 //
214 // SkFixed fx = ...;
215 // fx = tile(fx + SK_Fixed1);
216 //
217 // we want to make sure (fx + SK_Fixed1) never overflows.
218 if (fImage-> width() > 32767 ||
219 fImage->height() > 32767) {
220 return nullptr;
221 }
222
223 SkMatrix inv;
224 if (!this->computeTotalInverse(*rec.fMatrix, rec.fLocalMatrix, &inv) ||
225 !legacy_shader_can_handle(inv)) {
226 return nullptr;
227 }
228
229 if (!rec.isLegacyCompatible(fImage->colorSpace())) {
230 return nullptr;
231 }
232
233 return SkBitmapProcLegacyShader::MakeContext(*this, fTileModeX, fTileModeY, fSampling,
234 as_IB(fImage.get()), rec, alloc);
235 }
236 #endif
237
onIsAImage(SkMatrix * texM,SkTileMode xy[]) const238 SkImage* SkImageShader::onIsAImage(SkMatrix* texM, SkTileMode xy[]) const {
239 if (texM) {
240 *texM = this->getLocalMatrix();
241 }
242 if (xy) {
243 xy[0] = fTileModeX;
244 xy[1] = fTileModeY;
245 }
246 return const_cast<SkImage*>(fImage.get());
247 }
248
Make(sk_sp<SkImage> image,SkTileMode tmx,SkTileMode tmy,const SkSamplingOptions & options,const SkMatrix * localMatrix,bool clampAsIfUnpremul)249 sk_sp<SkShader> SkImageShader::Make(sk_sp<SkImage> image,
250 SkTileMode tmx, SkTileMode tmy,
251 const SkSamplingOptions& options,
252 const SkMatrix* localMatrix,
253 bool clampAsIfUnpremul) {
254 auto is_unit = [](float x) {
255 return x >= 0 && x <= 1;
256 };
257 if (options.useCubic) {
258 if (!is_unit(options.cubic.B) || !is_unit(options.cubic.C)) {
259 return nullptr;
260 }
261 }
262 if (!image) {
263 return sk_make_sp<SkEmptyShader>();
264 }
265 return sk_sp<SkShader>{
266 new SkImageShader(image, tmx, tmy, options, localMatrix, clampAsIfUnpremul)
267 };
268 }
269
270 ///////////////////////////////////////////////////////////////////////////////////////////////////
271
272 #if SK_SUPPORT_GPU
273
274 #include "src/gpu/GrColorInfo.h"
275 #include "src/gpu/effects/GrBlendFragmentProcessor.h"
276
asFragmentProcessor(const GrFPArgs & args) const277 std::unique_ptr<GrFragmentProcessor> SkImageShader::asFragmentProcessor(
278 const GrFPArgs& args) const {
279 const auto lm = this->totalLocalMatrix(args.fPreLocalMatrix);
280 SkMatrix lmInverse;
281 if (!lm->invert(&lmInverse)) {
282 return nullptr;
283 }
284
285 SkTileMode tileModes[2] = {fTileModeX, fTileModeY};
286 auto fp = as_IB(fImage.get())->asFragmentProcessor(args.fContext,
287 fSampling,
288 tileModes,
289 lmInverse);
290 if (!fp) {
291 return nullptr;
292 }
293
294 fp = GrColorSpaceXformEffect::Make(std::move(fp),
295 fImage->colorSpace(),
296 fImage->alphaType(),
297 args.fDstColorInfo->colorSpace(),
298 kPremul_SkAlphaType);
299 if (fImage->isAlphaOnly()) {
300 return GrBlendFragmentProcessor::Make(std::move(fp), nullptr, SkBlendMode::kDstIn);
301 } else {
302 return fp;
303 }
304 }
305
306 #endif
307
308 ///////////////////////////////////////////////////////////////////////////////////////////////////
309 #include "src/core/SkImagePriv.h"
310
SkMakeBitmapShaderForPaint(const SkPaint & paint,const SkBitmap & src,SkTileMode tmx,SkTileMode tmy,const SkSamplingOptions & sampling,const SkMatrix * localMatrix,SkCopyPixelsMode mode)311 sk_sp<SkShader> SkMakeBitmapShaderForPaint(const SkPaint& paint, const SkBitmap& src,
312 SkTileMode tmx, SkTileMode tmy,
313 const SkSamplingOptions& sampling,
314 const SkMatrix* localMatrix, SkCopyPixelsMode mode) {
315 auto s = SkImageShader::Make(SkMakeImageFromRasterBitmap(src, mode),
316 tmx, tmy, sampling, localMatrix);
317 if (!s) {
318 return nullptr;
319 }
320 if (src.colorType() == kAlpha_8_SkColorType && paint.getShader()) {
321 // Compose the image shader with the paint's shader. Alpha images+shaders should output the
322 // texture's alpha multiplied by the shader's color. DstIn (d*sa) will achieve this with
323 // the source image and dst shader (MakeBlend takes dst first, src second).
324 s = SkShaders::Blend(SkBlendMode::kDstIn, paint.refShader(), std::move(s));
325 }
326 return s;
327 }
328
RegisterFlattenables()329 void SkShaderBase::RegisterFlattenables() { SK_REGISTER_FLATTENABLE(SkImageShader); }
330
331 class SkImageShader::TransformShader : public SkTransformShader {
332 public:
TransformShader(const SkImageShader & shader)333 explicit TransformShader(const SkImageShader& shader)
334 : SkTransformShader{shader}
335 , fImageShader{shader} {}
336
onProgram(skvm::Builder * b,skvm::Coord device,skvm::Coord local,skvm::Color color,const SkMatrixProvider & matrices,const SkMatrix * localM,const SkColorInfo & dst,skvm::Uniforms * uniforms,SkArenaAlloc * alloc) const337 skvm::Color onProgram(skvm::Builder* b,
338 skvm::Coord device, skvm::Coord local, skvm::Color color,
339 const SkMatrixProvider& matrices, const SkMatrix* localM,
340 const SkColorInfo& dst,
341 skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override {
342 return fImageShader.makeProgram(
343 b, device, local, color, matrices, localM, dst, uniforms, this, alloc);
344 }
345
346 private:
347 const SkImageShader& fImageShader;
348 };
349
tweak_sampling(SkSamplingOptions sampling,const SkMatrix & matrix)350 static SkSamplingOptions tweak_sampling(SkSamplingOptions sampling, const SkMatrix& matrix) {
351 SkFilterMode filter = sampling.filter;
352
353 // When the matrix is just an integer translate, bilerp == nearest neighbor.
354 if (filter == SkFilterMode::kLinear &&
355 matrix.getType() <= SkMatrix::kTranslate_Mask &&
356 matrix.getTranslateX() == (int)matrix.getTranslateX() &&
357 matrix.getTranslateY() == (int)matrix.getTranslateY()) {
358 filter = SkFilterMode::kNearest;
359 }
360
361 return SkSamplingOptions(filter, sampling.mipmap);
362 }
363
tweak_inv_matrix(SkFilterMode filter,SkMatrix matrix)364 static SkMatrix tweak_inv_matrix(SkFilterMode filter, SkMatrix matrix) {
365 // See skia:4649 and the GM image_scale_aligned.
366 if (filter == SkFilterMode::kNearest) {
367 if (matrix.getScaleX() >= 0) {
368 matrix.setTranslateX(nextafterf(matrix.getTranslateX(),
369 floorf(matrix.getTranslateX())));
370 }
371 if (matrix.getScaleY() >= 0) {
372 matrix.setTranslateY(nextafterf(matrix.getTranslateY(),
373 floorf(matrix.getTranslateY())));
374 }
375 }
376 return matrix;
377 }
378
doStages(const SkStageRec & rec,TransformShader * updater) const379 bool SkImageShader::doStages(const SkStageRec& rec, TransformShader* updater) const {
380 // We only support certain sampling options in stages so far
381 auto sampling = fSampling;
382 if (sampling.useCubic) {
383 if (!is_default_cubic_resampler(sampling.cubic)) {
384 return false;
385 }
386 } else if (sampling.mipmap == SkMipmapMode::kLinear) {
387 return false;
388 }
389
390
391 if (updater && (sampling.mipmap != SkMipmapMode::kNone)) {
392 // TODO: medium: recall RequestBitmap and update width/height accordingly
393 return false;
394 }
395
396 SkRasterPipeline* p = rec.fPipeline;
397 SkArenaAlloc* alloc = rec.fAlloc;
398
399 SkMatrix matrix;
400 if (!this->computeTotalInverse(rec.fMatrixProvider.localToDevice(), rec.fLocalM, &matrix)) {
401 return false;
402 }
403 matrix.normalizePerspective();
404
405 SkASSERT(!sampling.useCubic || sampling.mipmap == SkMipmapMode::kNone);
406 auto* access = SkMipmapAccessor::Make(alloc, fImage.get(), matrix, sampling.mipmap);
407 if (!access) {
408 return false;
409 }
410 SkPixmap pm;
411 std::tie(pm, matrix) = access->level();
412
413 p->append(SkRasterPipeline::seed_shader);
414
415 if (updater) {
416 updater->appendMatrix(rec.fMatrixProvider.localToDevice(), p);
417 } else {
418 if (!sampling.useCubic) {
419 // TODO: can tweak_sampling sometimes for cubic too when B=0
420 if (rec.fMatrixProvider.localToDeviceHitsPixelCenters()) {
421 sampling = tweak_sampling(sampling, matrix);
422 }
423 matrix = tweak_inv_matrix(sampling.filter, matrix);
424 }
425 p->append_matrix(alloc, matrix);
426 }
427
428 auto gather = alloc->make<SkRasterPipeline_GatherCtx>();
429 gather->pixels = pm.addr();
430 gather->stride = pm.rowBytesAsPixels();
431 gather->width = pm.width();
432 gather->height = pm.height();
433
434 auto limit_x = alloc->make<SkRasterPipeline_TileCtx>(),
435 limit_y = alloc->make<SkRasterPipeline_TileCtx>();
436 limit_x->scale = pm.width();
437 limit_x->invScale = 1.0f / pm.width();
438 limit_y->scale = pm.height();
439 limit_y->invScale = 1.0f / pm.height();
440
441 SkRasterPipeline_DecalTileCtx* decal_ctx = nullptr;
442 bool decal_x_and_y = fTileModeX == SkTileMode::kDecal && fTileModeY == SkTileMode::kDecal;
443 if (fTileModeX == SkTileMode::kDecal || fTileModeY == SkTileMode::kDecal) {
444 decal_ctx = alloc->make<SkRasterPipeline_DecalTileCtx>();
445 decal_ctx->limit_x = limit_x->scale;
446 decal_ctx->limit_y = limit_y->scale;
447 }
448
449 auto append_tiling_and_gather = [&] {
450 if (decal_x_and_y) {
451 p->append(SkRasterPipeline::decal_x_and_y, decal_ctx);
452 } else {
453 switch (fTileModeX) {
454 case SkTileMode::kClamp: /* The gather_xxx stage will clamp for us. */ break;
455 case SkTileMode::kMirror: p->append(SkRasterPipeline::mirror_x, limit_x); break;
456 case SkTileMode::kRepeat: p->append(SkRasterPipeline::repeat_x, limit_x); break;
457 case SkTileMode::kDecal: p->append(SkRasterPipeline::decal_x, decal_ctx); break;
458 }
459 switch (fTileModeY) {
460 case SkTileMode::kClamp: /* The gather_xxx stage will clamp for us. */ break;
461 case SkTileMode::kMirror: p->append(SkRasterPipeline::mirror_y, limit_y); break;
462 case SkTileMode::kRepeat: p->append(SkRasterPipeline::repeat_y, limit_y); break;
463 case SkTileMode::kDecal: p->append(SkRasterPipeline::decal_y, decal_ctx); break;
464 }
465 }
466
467 void* ctx = gather;
468 switch (pm.colorType()) {
469 case kAlpha_8_SkColorType: p->append(SkRasterPipeline::gather_a8, ctx); break;
470 case kA16_unorm_SkColorType: p->append(SkRasterPipeline::gather_a16, ctx); break;
471 case kA16_float_SkColorType: p->append(SkRasterPipeline::gather_af16, ctx); break;
472 case kRGB_565_SkColorType: p->append(SkRasterPipeline::gather_565, ctx); break;
473 case kARGB_4444_SkColorType: p->append(SkRasterPipeline::gather_4444, ctx); break;
474 case kR8G8_unorm_SkColorType: p->append(SkRasterPipeline::gather_rg88, ctx); break;
475 case kR16G16_unorm_SkColorType: p->append(SkRasterPipeline::gather_rg1616, ctx); break;
476 case kR16G16_float_SkColorType: p->append(SkRasterPipeline::gather_rgf16, ctx); break;
477 case kRGBA_8888_SkColorType: p->append(SkRasterPipeline::gather_8888, ctx); break;
478 case kRGBA_1010102_SkColorType: p->append(SkRasterPipeline::gather_1010102, ctx); break;
479 case kR16G16B16A16_unorm_SkColorType:
480 p->append(SkRasterPipeline::gather_16161616,ctx); break;
481 case kRGBA_F16Norm_SkColorType:
482 case kRGBA_F16_SkColorType: p->append(SkRasterPipeline::gather_f16, ctx); break;
483 case kRGBA_F32_SkColorType: p->append(SkRasterPipeline::gather_f32, ctx); break;
484
485 case kGray_8_SkColorType: p->append(SkRasterPipeline::gather_a8, ctx);
486 p->append(SkRasterPipeline::alpha_to_gray ); break;
487
488 case kRGB_888x_SkColorType: p->append(SkRasterPipeline::gather_8888, ctx);
489 p->append(SkRasterPipeline::force_opaque ); break;
490
491 case kBGRA_1010102_SkColorType: p->append(SkRasterPipeline::gather_1010102, ctx);
492 p->append(SkRasterPipeline::swap_rb ); break;
493
494 case kRGB_101010x_SkColorType: p->append(SkRasterPipeline::gather_1010102, ctx);
495 p->append(SkRasterPipeline::force_opaque ); break;
496
497 case kBGR_101010x_SkColorType: p->append(SkRasterPipeline::gather_1010102, ctx);
498 p->append(SkRasterPipeline::force_opaque );
499 p->append(SkRasterPipeline::swap_rb ); break;
500
501 case kBGRA_8888_SkColorType: p->append(SkRasterPipeline::gather_8888, ctx);
502 p->append(SkRasterPipeline::swap_rb ); break;
503
504 case kSRGBA_8888_SkColorType:
505 p->append(SkRasterPipeline::gather_8888, ctx);
506 p->append_transfer_function(*skcms_sRGB_TransferFunction());
507 break;
508
509 case kUnknown_SkColorType: SkASSERT(false);
510 }
511 if (decal_ctx) {
512 p->append(SkRasterPipeline::check_decal_mask, decal_ctx);
513 }
514 };
515
516 auto append_misc = [&] {
517 SkColorSpace* cs = pm.colorSpace();
518 SkAlphaType at = pm.alphaType();
519
520 // Color for A8 images comes from the paint. TODO: all alpha images? none?
521 if (pm.colorType() == kAlpha_8_SkColorType) {
522 SkColor4f rgb = rec.fPaint.getColor4f();
523 p->append_set_rgb(alloc, rgb);
524
525 cs = sk_srgb_singleton();
526 at = kUnpremul_SkAlphaType;
527 }
528
529 // Bicubic filtering naturally produces out of range values on both sides of [0,1].
530 if (sampling.useCubic) {
531 p->append(SkRasterPipeline::clamp_0);
532 p->append(at == kUnpremul_SkAlphaType || fClampAsIfUnpremul
533 ? SkRasterPipeline::clamp_1
534 : SkRasterPipeline::clamp_a);
535 }
536
537 // Transform color space and alpha type to match shader convention (dst CS, premul alpha).
538 alloc->make<SkColorSpaceXformSteps>(cs, at,
539 rec.fDstCS, kPremul_SkAlphaType)
540 ->apply(p);
541
542 return true;
543 };
544
545 // Check for fast-path stages.
546 auto ct = pm.colorType();
547 if (true
548 && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType)
549 && !sampling.useCubic && sampling.filter == SkFilterMode::kLinear
550 && fTileModeX == SkTileMode::kClamp && fTileModeY == SkTileMode::kClamp) {
551
552 p->append(SkRasterPipeline::bilerp_clamp_8888, gather);
553 if (ct == kBGRA_8888_SkColorType) {
554 p->append(SkRasterPipeline::swap_rb);
555 }
556 return append_misc();
557 }
558 if (true
559 && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType) // TODO: all formats
560 && !sampling.useCubic && sampling.filter == SkFilterMode::kLinear
561 && fTileModeX != SkTileMode::kDecal // TODO decal too?
562 && fTileModeY != SkTileMode::kDecal) {
563
564 auto ctx = alloc->make<SkRasterPipeline_SamplerCtx2>();
565 *(SkRasterPipeline_GatherCtx*)(ctx) = *gather;
566 ctx->ct = ct;
567 ctx->tileX = fTileModeX;
568 ctx->tileY = fTileModeY;
569 ctx->invWidth = 1.0f / ctx->width;
570 ctx->invHeight = 1.0f / ctx->height;
571 p->append(SkRasterPipeline::bilinear, ctx);
572 return append_misc();
573 }
574 if (true
575 && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType)
576 && sampling.useCubic
577 && fTileModeX == SkTileMode::kClamp && fTileModeY == SkTileMode::kClamp) {
578
579 p->append(SkRasterPipeline::bicubic_clamp_8888, gather);
580 if (ct == kBGRA_8888_SkColorType) {
581 p->append(SkRasterPipeline::swap_rb);
582 }
583 return append_misc();
584 }
585 if (true
586 && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType) // TODO: all formats
587 && sampling.useCubic
588 && fTileModeX != SkTileMode::kDecal // TODO decal too?
589 && fTileModeY != SkTileMode::kDecal) {
590
591 auto ctx = alloc->make<SkRasterPipeline_SamplerCtx2>();
592 *(SkRasterPipeline_GatherCtx*)(ctx) = *gather;
593 ctx->ct = ct;
594 ctx->tileX = fTileModeX;
595 ctx->tileY = fTileModeY;
596 ctx->invWidth = 1.0f / ctx->width;
597 ctx->invHeight = 1.0f / ctx->height;
598 p->append(SkRasterPipeline::bicubic, ctx);
599 return append_misc();
600 }
601
602 SkRasterPipeline_SamplerCtx* sampler = alloc->make<SkRasterPipeline_SamplerCtx>();
603
604 auto sample = [&](SkRasterPipeline::StockStage setup_x,
605 SkRasterPipeline::StockStage setup_y) {
606 p->append(setup_x, sampler);
607 p->append(setup_y, sampler);
608 append_tiling_and_gather();
609 p->append(SkRasterPipeline::accumulate, sampler);
610 };
611
612 if (sampling.useCubic) {
613 p->append(SkRasterPipeline::save_xy, sampler);
614
615 sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n3y);
616 sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n3y);
617 sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n3y);
618 sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n3y);
619
620 sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n1y);
621 sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n1y);
622 sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n1y);
623 sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n1y);
624
625 sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p1y);
626 sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p1y);
627 sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p1y);
628 sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p1y);
629
630 sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p3y);
631 sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p3y);
632 sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p3y);
633 sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p3y);
634
635 p->append(SkRasterPipeline::move_dst_src);
636 } else if (sampling.filter == SkFilterMode::kLinear) {
637 p->append(SkRasterPipeline::save_xy, sampler);
638
639 sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_ny);
640 sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_ny);
641 sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_py);
642 sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_py);
643
644 p->append(SkRasterPipeline::move_dst_src);
645 } else {
646 append_tiling_and_gather();
647 }
648
649 return append_misc();
650 }
651
onAppendStages(const SkStageRec & rec) const652 bool SkImageShader::onAppendStages(const SkStageRec& rec) const {
653 return this->doStages(rec, nullptr);
654 }
655
onAppendUpdatableStages(const SkStageRec & rec) const656 SkStageUpdater* SkImageShader::onAppendUpdatableStages(const SkStageRec& rec) const {
657 TransformShader* updater = rec.fAlloc->make<TransformShader>(*this);
658 return this->doStages(rec, updater) ? updater : nullptr;
659 }
660
onUpdatableShader(SkArenaAlloc * alloc) const661 SkUpdatableShader* SkImageShader::onUpdatableShader(SkArenaAlloc* alloc) const {
662 return alloc->make<TransformShader>(*this);
663 }
664
onProgram(skvm::Builder * b,skvm::Coord device,skvm::Coord origLocal,skvm::Color paint,const SkMatrixProvider & matrices,const SkMatrix * localM,const SkColorInfo & dst,skvm::Uniforms * uniforms,SkArenaAlloc * alloc) const665 skvm::Color SkImageShader::onProgram(skvm::Builder* b,
666 skvm::Coord device, skvm::Coord origLocal, skvm::Color paint,
667 const SkMatrixProvider& matrices, const SkMatrix* localM,
668 const SkColorInfo& dst,
669 skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
670 return this->makeProgram(
671 b, device, origLocal, paint, matrices, localM, dst, uniforms, nullptr, alloc);
672 }
673
makeProgram(skvm::Builder * p,skvm::Coord device,skvm::Coord origLocal,skvm::Color paint,const SkMatrixProvider & matrices,const SkMatrix * localM,const SkColorInfo & dst,skvm::Uniforms * uniforms,const TransformShader * coordShader,SkArenaAlloc * alloc) const674 skvm::Color SkImageShader::makeProgram(
675 skvm::Builder* p, skvm::Coord device, skvm::Coord origLocal, skvm::Color paint,
676 const SkMatrixProvider& matrices, const SkMatrix* localM, const SkColorInfo& dst,
677 skvm::Uniforms* uniforms, const TransformShader* coordShader, SkArenaAlloc* alloc) const {
678
679 SkMatrix baseInv;
680 if (!this->computeTotalInverse(matrices.localToDevice(), localM, &baseInv)) {
681 return {};
682 }
683 baseInv.normalizePerspective();
684
685 auto sampling = fSampling;
686 auto* access = SkMipmapAccessor::Make(alloc, fImage.get(), baseInv, sampling.mipmap);
687 if (!access) {
688 return {};
689 }
690 auto [upper, upperInv] = access->level();
691 // If we are using a coordShader, then we can't make guesses about the state of the matrix.
692 if (!sampling.useCubic && !coordShader) {
693 // TODO: can tweak_sampling sometimes for cubic too when B=0
694 if (matrices.localToDeviceHitsPixelCenters()) {
695 sampling = tweak_sampling(sampling, upperInv);
696 }
697 upperInv = tweak_inv_matrix(sampling.filter, upperInv);
698 }
699
700 SkPixmap lowerPixmap;
701 SkMatrix lowerInv;
702 SkPixmap* lower = nullptr;
703 float lowerWeight = access->lowerWeight();
704 if (lowerWeight > 0) {
705 std::tie(lowerPixmap, lowerInv) = access->lowerLevel();
706 lower = &lowerPixmap;
707 }
708
709 skvm::Coord upperLocal;
710 if (coordShader != nullptr) {
711 upperLocal = coordShader->applyMatrix(p, upperInv, origLocal, uniforms);
712 } else {
713 upperLocal = SkShaderBase::ApplyMatrix(p, upperInv, origLocal, uniforms);
714 }
715
716 // We can exploit image opacity to skip work unpacking alpha channels.
717 const bool input_is_opaque = SkAlphaTypeIsOpaque(upper.alphaType())
718 || SkColorTypeIsAlwaysOpaque(upper.colorType());
719
720 // Each call to sample() will try to rewrite the same uniforms over and over,
721 // so remember where we start and reset back there each time. That way each
722 // sample() call uses the same uniform offsets.
723
724 auto compute_clamp_limit = [&](float limit) {
725 // Subtract an ulp so the upper clamp limit excludes limit itself.
726 int bits;
727 memcpy(&bits, &limit, 4);
728 return p->uniformF(uniforms->push(bits-1));
729 };
730
731 // Except in the simplest case (no mips, no filtering), we reference uniforms
732 // more than once. To avoid adding/registering them multiple times, we pre-load them
733 // into a struct (just to logically group them together), based on the "current"
734 // pixmap (level of a mipmap).
735 //
736 struct Uniforms {
737 skvm::F32 w, iw, i2w,
738 h, ih, i2h;
739
740 skvm::F32 clamp_w,
741 clamp_h;
742
743 skvm::Uniform addr;
744 skvm::I32 rowBytesAsPixels;
745
746 skvm::PixelFormat pixelFormat; // not a uniform, but needed for each texel sample,
747 // so we store it here, since it is also dependent on
748 // the current pixmap (level).
749 };
750
751 auto setup_uniforms = [&](const SkPixmap& pm) -> Uniforms {
752 skvm::PixelFormat pixelFormat = skvm::SkColorType_to_PixelFormat(pm.colorType());
753 return {
754 p->uniformF(uniforms->pushF( pm.width())),
755 p->uniformF(uniforms->pushF(1.0f/pm.width())), // iff tileX == kRepeat
756 p->uniformF(uniforms->pushF(0.5f/pm.width())), // iff tileX == kMirror
757
758 p->uniformF(uniforms->pushF( pm.height())),
759 p->uniformF(uniforms->pushF(1.0f/pm.height())), // iff tileY == kRepeat
760 p->uniformF(uniforms->pushF(0.5f/pm.height())), // iff tileY == kMirror
761
762 compute_clamp_limit(pm. width()),
763 compute_clamp_limit(pm.height()),
764
765 uniforms->pushPtr(pm.addr()),
766 p->uniform32(uniforms->push(pm.rowBytesAsPixels())),
767
768 pixelFormat,
769 };
770 };
771
772 auto sample_texel = [&](const Uniforms& u, skvm::F32 sx, skvm::F32 sy) -> skvm::Color {
773 // repeat() and mirror() are written assuming they'll be followed by a [0,scale) clamp.
774 auto repeat = [&](skvm::F32 v, skvm::F32 S, skvm::F32 I) {
775 return v - floor(v * I) * S;
776 };
777 auto mirror = [&](skvm::F32 v, skvm::F32 S, skvm::F32 I2) {
778 // abs( (v-scale) - (2*scale)*floor((v-scale)*(0.5f/scale)) - scale )
779 // {---A---} {------------------B------------------}
780 skvm::F32 A = v - S,
781 B = (S + S) * floor(A * I2);
782 return abs(A - B - S);
783 };
784 switch (fTileModeX) {
785 case SkTileMode::kDecal: /* handled after gather */ break;
786 case SkTileMode::kClamp: /* we always clamp */ break;
787 case SkTileMode::kRepeat: sx = repeat(sx, u.w, u.iw); break;
788 case SkTileMode::kMirror: sx = mirror(sx, u.w, u.i2w); break;
789 }
790 switch (fTileModeY) {
791 case SkTileMode::kDecal: /* handled after gather */ break;
792 case SkTileMode::kClamp: /* we always clamp */ break;
793 case SkTileMode::kRepeat: sy = repeat(sy, u.h, u.ih); break;
794 case SkTileMode::kMirror: sy = mirror(sy, u.h, u.i2h); break;
795 }
796
797 // Always clamp sample coordinates to [0,width), [0,height), both for memory
798 // safety and to handle the clamps still needed by kClamp, kRepeat, and kMirror.
799 skvm::F32 clamped_x = clamp(sx, 0, u.clamp_w),
800 clamped_y = clamp(sy, 0, u.clamp_h);
801
802 // Load pixels from pm.addr()[(int)sx + (int)sy*stride].
803 skvm::I32 index = trunc(clamped_x) +
804 trunc(clamped_y) * u.rowBytesAsPixels;
805 skvm::Color c = gather(u.pixelFormat, u.addr, index);
806
807 // If we know the image is opaque, jump right to alpha = 1.0f, skipping work to unpack it.
808 if (input_is_opaque) {
809 c.a = p->splat(1.0f);
810 }
811
812 // Mask away any pixels that we tried to sample outside the bounds in kDecal.
813 if (fTileModeX == SkTileMode::kDecal || fTileModeY == SkTileMode::kDecal) {
814 skvm::I32 mask = p->splat(~0);
815 if (fTileModeX == SkTileMode::kDecal) { mask &= (sx == clamped_x); }
816 if (fTileModeY == SkTileMode::kDecal) { mask &= (sy == clamped_y); }
817 c.r = pun_to_F32(p->bit_and(mask, pun_to_I32(c.r)));
818 c.g = pun_to_F32(p->bit_and(mask, pun_to_I32(c.g)));
819 c.b = pun_to_F32(p->bit_and(mask, pun_to_I32(c.b)));
820 c.a = pun_to_F32(p->bit_and(mask, pun_to_I32(c.a)));
821 // Notice that even if input_is_opaque, c.a might now be 0.
822 }
823
824 return c;
825 };
826
827 auto sample_level = [&](const SkPixmap& pm, const SkMatrix& inv, skvm::Coord local) {
828 const Uniforms u = setup_uniforms(pm);
829
830 if (sampling.useCubic) {
831 // All bicubic samples have the same fractional offset (fx,fy) from the center.
832 // They're either the 16 corners of a 3x3 grid/ surrounding (x,y) at (0.5,0.5) off-center.
833 skvm::F32 fx = fract(local.x + 0.5f),
834 fy = fract(local.y + 0.5f);
835 skvm::F32 wx[4],
836 wy[4];
837
838 SkM44 weights = CubicResamplerMatrix(sampling.cubic.B, sampling.cubic.C);
839
840 auto dot = [](const skvm::F32 a[], const skvm::F32 b[]) {
841 return a[0]*b[0] + a[1]*b[1] + a[2]*b[2] + a[3]*b[3];
842 };
843 const skvm::F32 tmpx[] = { p->splat(1.0f), fx, fx*fx, fx*fx*fx };
844 const skvm::F32 tmpy[] = { p->splat(1.0f), fy, fy*fy, fy*fy*fy };
845
846 for (int row = 0; row < 4; ++row) {
847 SkV4 r = weights.row(row);
848 skvm::F32 ru[] = {
849 p->uniformF(uniforms->pushF(r[0])),
850 p->uniformF(uniforms->pushF(r[1])),
851 p->uniformF(uniforms->pushF(r[2])),
852 p->uniformF(uniforms->pushF(r[3])),
853 };
854 wx[row] = dot(ru, tmpx);
855 wy[row] = dot(ru, tmpy);
856 }
857
858 skvm::Color c;
859 c.r = c.g = c.b = c.a = p->splat(0.0f);
860
861 skvm::F32 sy = local.y - 1.5f;
862 for (int j = 0; j < 4; j++, sy += 1.0f) {
863 skvm::F32 sx = local.x - 1.5f;
864 for (int i = 0; i < 4; i++, sx += 1.0f) {
865 skvm::Color s = sample_texel(u, sx,sy);
866 skvm::F32 w = wx[i] * wy[j];
867
868 c.r += s.r * w;
869 c.g += s.g * w;
870 c.b += s.b * w;
871 c.a += s.a * w;
872 }
873 }
874 return c;
875 } else if (sampling.filter == SkFilterMode::kLinear) {
876 // Our four sample points are the corners of a logical 1x1 pixel
877 // box surrounding (x,y) at (0.5,0.5) off-center.
878 skvm::F32 left = local.x - 0.5f,
879 top = local.y - 0.5f,
880 right = local.x + 0.5f,
881 bottom = local.y + 0.5f;
882
883 // The fractional parts of right and bottom are our lerp factors in x and y respectively.
884 skvm::F32 fx = fract(right ),
885 fy = fract(bottom);
886
887 return lerp(lerp(sample_texel(u, left,top ), sample_texel(u, right,top ), fx),
888 lerp(sample_texel(u, left,bottom), sample_texel(u, right,bottom), fx), fy);
889 } else {
890 SkASSERT(sampling.filter == SkFilterMode::kNearest);
891 return sample_texel(u, local.x,local.y);
892 }
893 };
894
895 skvm::Color c = sample_level(upper, upperInv, upperLocal);
896 if (lower) {
897 auto lowerLocal = SkShaderBase::ApplyMatrix(p, lowerInv, origLocal, uniforms);
898 // lower * weight + upper * (1 - weight)
899 c = lerp(c,
900 sample_level(*lower, lowerInv, lowerLocal),
901 p->uniformF(uniforms->pushF(lowerWeight)));
902 }
903
904 // If the input is opaque and we're not in decal mode, that means the output is too.
905 // Forcing *a to 1.0 here will retroactively skip any work we did to interpolate sample alphas.
906 if (input_is_opaque
907 && fTileModeX != SkTileMode::kDecal
908 && fTileModeY != SkTileMode::kDecal) {
909 c.a = p->splat(1.0f);
910 }
911
912 // Alpha-only images get their color from the paint (already converted to dst color space).
913 SkColorSpace* cs = upper.colorSpace();
914 SkAlphaType at = upper.alphaType();
915 if (SkColorTypeIsAlphaOnly(upper.colorType())) {
916 c.r = paint.r;
917 c.g = paint.g;
918 c.b = paint.b;
919
920 cs = dst.colorSpace();
921 at = kUnpremul_SkAlphaType;
922 }
923
924 if (sampling.useCubic) {
925 // Bicubic filtering naturally produces out of range values on both sides of [0,1].
926 c.a = clamp01(c.a);
927
928 skvm::F32 limit = (at == kUnpremul_SkAlphaType || fClampAsIfUnpremul)
929 ? p->splat(1.0f)
930 : c.a;
931 c.r = clamp(c.r, 0.0f, limit);
932 c.g = clamp(c.g, 0.0f, limit);
933 c.b = clamp(c.b, 0.0f, limit);
934 }
935
936 return SkColorSpaceXformSteps{cs,at, dst.colorSpace(),dst.alphaType()}.program(p, uniforms, c);
937 }
938