blob: d126df0ebff13ca3e885cc7746b4cb1d8ee4b3fd [file] [log] [blame]
reed856e9d92015-09-30 12:21:45 -07001/*
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
Brian Salomon0c243202020-06-29 14:29:25 -04008#include "src/shaders/SkImageShader.h"
9
Ben Wagner729a23f2019-05-17 16:29:34 -040010#include "src/core/SkArenaAlloc.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "src/core/SkColorSpacePriv.h"
12#include "src/core/SkColorSpaceXformSteps.h"
Mike Reed8c1ad7e2020-12-02 20:41:52 -050013#include "src/core/SkMatrixPriv.h"
Brian Osman449b1152020-04-15 16:43:00 -040014#include "src/core/SkMatrixProvider.h"
Mike Reed60a2ec02020-12-08 09:18:14 -050015#include "src/core/SkMipmapAccessor.h"
Mike Klein37bc8f92019-10-21 13:10:07 -050016#include "src/core/SkOpts.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050017#include "src/core/SkRasterPipeline.h"
18#include "src/core/SkReadBuffer.h"
Mike Reed92236652021-02-01 13:07:32 -050019#include "src/core/SkSamplingPriv.h"
Brian Salomon0c243202020-06-29 14:29:25 -040020#include "src/core/SkScopeExit.h"
Mike Klein8e717442020-01-07 10:22:33 -060021#include "src/core/SkVM.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050022#include "src/core/SkWriteBuffer.h"
23#include "src/image/SkImage_Base.h"
24#include "src/shaders/SkBitmapProcShader.h"
25#include "src/shaders/SkEmptyShader.h"
Herb Derby5992f9e2021-07-17 18:19:03 -040026#include "src/shaders/SkTransformShader.h"
reed856e9d92015-09-30 12:21:45 -070027
Mike Reed3d30ca62020-07-22 16:55:02 -040028SkM44 SkImageShader::CubicResamplerMatrix(float B, float C) {
Mike Reed3867c702020-09-01 13:28:10 -040029#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);
Mike Reed3d30ca62020-07-22 16:55:02 -040051}
52
Mike Reed587d0822017-06-23 16:49:12 -040053/**
54 * We are faster in clamp, so always use that tiling when we can.
55 */
Mike Reedfae8fce2019-04-03 10:27:45 -040056static SkTileMode optimize(SkTileMode tm, int dimension) {
Mike Reed587d0822017-06-23 16:49:12 -040057 SkASSERT(dimension > 0);
Mike Reed2e3c9552017-06-23 21:33:58 -040058#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.
Mike Reedfae8fce2019-04-03 10:27:45 -040061 return tm;
Mike Reed2e3c9552017-06-23 21:33:58 -040062#else
Mike Reedfae8fce2019-04-03 10:27:45 -040063 return dimension == 1 ? SkTileMode::kClamp : tm;
Mike Reed2e3c9552017-06-23 21:33:58 -040064#endif
Mike Reed587d0822017-06-23 16:49:12 -040065}
66
Mike Klein1f313092018-01-03 10:30:21 -050067SkImageShader::SkImageShader(sk_sp<SkImage> img,
Mike Reede25b4472019-04-02 17:49:12 -040068 SkTileMode tmx, SkTileMode tmy,
Mike Reed225cdea2021-03-18 22:19:44 -040069 const SkSamplingOptions& sampling,
Mike Klein1f313092018-01-03 10:30:21 -050070 const SkMatrix* localMatrix,
71 bool clampAsIfUnpremul)
72 : INHERITED(localMatrix)
reed6b2d7ac2016-08-11 06:42:26 -070073 , fImage(std::move(img))
Mike Reed225cdea2021-03-18 22:19:44 -040074 , fSampling(sampling)
Mike Reed587d0822017-06-23 16:49:12 -040075 , fTileModeX(optimize(tmx, fImage->width()))
76 , fTileModeY(optimize(tmy, fImage->height()))
Mike Klein1f313092018-01-03 10:30:21 -050077 , fClampAsIfUnpremul(clampAsIfUnpremul)
reed856e9d92015-09-30 12:21:45 -070078{}
79
Mike Reed74c51ac2020-11-20 10:23:58 -050080// just used for legacy-unflattening
81enum class LegacyFilterEnum {
82 kNone,
83 kLow,
84 kMedium,
85 kHigh,
86 // this is the special value for backward compatibility
87 kInheritFromPaint,
88 // this signals we should use the new SkFilterOptions
89 kUseFilterOptions,
Mike Kleinbb1933e2020-12-02 15:45:29 -060090 // use cubic and ignore FilterOptions
Mike Reed74c51ac2020-11-20 10:23:58 -050091 kUseCubicResampler,
Mike Klein1f313092018-01-03 10:30:21 -050092
Mike Reed74c51ac2020-11-20 10:23:58 -050093 kLast = kUseCubicResampler,
94};
95
Mike Reed74c51ac2020-11-20 10:23:58 -050096// fClampAsIfUnpremul is always false when constructed through public APIs,
97// so there's no need to read or write it here.
98
99sk_sp<SkFlattenable> SkImageShader::CreateProc(SkReadBuffer& buffer) {
Mike Reed74c51ac2020-11-20 10:23:58 -0500100 auto tmx = buffer.read32LE<SkTileMode>(SkTileMode::kLastTileMode);
101 auto tmy = buffer.read32LE<SkTileMode>(SkTileMode::kLastTileMode);
102
Mike Reed225cdea2021-03-18 22:19:44 -0400103 SkSamplingOptions sampling;
104 bool readSampling = true;
105 if (buffer.isVersionLT(SkPicturePriv::kNoFilterQualityShaders_Version) &&
106 !buffer.readBool() /* legacy has_sampling */)
107 {
108 readSampling = false;
109 // we just default to Nearest in sampling
110 }
111 if (readSampling) {
Mike Reed92236652021-02-01 13:07:32 -0500112 sampling = SkSamplingPriv::Read(buffer);
Mike Reed74c51ac2020-11-20 10:23:58 -0500113 }
114
115 SkMatrix localMatrix;
116 buffer.readMatrix(&localMatrix);
117 sk_sp<SkImage> img = buffer.readImage();
118 if (!img) {
119 return nullptr;
120 }
121
Mike Reed225cdea2021-03-18 22:19:44 -0400122 return SkImageShader::Make(std::move(img), tmx, tmy, sampling, &localMatrix);
reed856e9d92015-09-30 12:21:45 -0700123}
124
125void SkImageShader::flatten(SkWriteBuffer& buffer) const {
Mike Reedfae8fce2019-04-03 10:27:45 -0400126 buffer.writeUInt((unsigned)fTileModeX);
127 buffer.writeUInt((unsigned)fTileModeY);
Mike Reed74c51ac2020-11-20 10:23:58 -0500128
Mike Reed225cdea2021-03-18 22:19:44 -0400129 SkSamplingPriv::Write(buffer, fSampling);
Mike Reed74c51ac2020-11-20 10:23:58 -0500130
reed856e9d92015-09-30 12:21:45 -0700131 buffer.writeMatrix(this->getLocalMatrix());
reed6b2d7ac2016-08-11 06:42:26 -0700132 buffer.writeImage(fImage.get());
Mike Klein1f313092018-01-03 10:30:21 -0500133 SkASSERT(fClampAsIfUnpremul == false);
reed856e9d92015-09-30 12:21:45 -0700134}
135
136bool SkImageShader::isOpaque() const {
Mike Reedfae8fce2019-04-03 10:27:45 -0400137 return fImage->isOpaque() &&
138 fTileModeX != SkTileMode::kDecal && fTileModeY != SkTileMode::kDecal;
reed856e9d92015-09-30 12:21:45 -0700139}
140
Mike Reed74c51ac2020-11-20 10:23:58 -0500141constexpr SkCubicResampler kDefaultCubicResampler{1.0f/3, 1.0f/3};
142
143static bool is_default_cubic_resampler(SkCubicResampler cubic) {
144 return SkScalarNearlyEqual(cubic.B, kDefaultCubicResampler.B) &&
145 SkScalarNearlyEqual(cubic.C, kDefaultCubicResampler.C);
146}
147
Mike Reed604e4c22020-11-25 21:08:17 -0500148#ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
Mike Reed74c51ac2020-11-20 10:23:58 -0500149
Florin Malitaaf2769d2018-04-04 13:46:35 -0400150static bool legacy_shader_can_handle(const SkMatrix& inv) {
Mike Reed81063112020-06-09 17:36:33 -0400151 SkASSERT(!inv.hasPerspective());
Mike Klein37bc8f92019-10-21 13:10:07 -0500152
153 // Scale+translate methods are always present, but affine might not be.
154 if (!SkOpts::S32_alpha_D32_filter_DXDY && !inv.isScaleTranslate()) {
Mike Reeda12c4192018-02-01 16:34:03 -0500155 return false;
156 }
157
158 // legacy code uses SkFixed 32.32, so ensure the inverse doesn't map device coordinates
159 // out of range.
160 const SkScalar max_dev_coord = 32767.0f;
Mike Klein37bc8f92019-10-21 13:10:07 -0500161 const SkRect src = inv.mapRect(SkRect::MakeWH(max_dev_coord, max_dev_coord));
Mike Reeda12c4192018-02-01 16:34:03 -0500162
Mike Reed1eb5ca42018-03-08 14:20:52 -0500163 // take 1/4 of max signed 32bits so we have room to subtract local values
Kevin Lubickf76da632020-01-28 10:39:56 -0500164 const SkScalar max_fixed32dot32 = float(SK_MaxS32) * 0.25f;
Mike Reeda12c4192018-02-01 16:34:03 -0500165 if (!SkRect::MakeLTRB(-max_fixed32dot32, -max_fixed32dot32,
Mike Klein37bc8f92019-10-21 13:10:07 -0500166 +max_fixed32dot32, +max_fixed32dot32).contains(src)) {
Mike Reeda12c4192018-02-01 16:34:03 -0500167 return false;
168 }
169
170 // legacy shader impl should be able to handle these matrices
171 return true;
172}
173
Florin Malita4aed1382017-05-25 10:38:07 -0400174SkShaderBase::Context* SkImageShader::onMakeContext(const ContextRec& rec,
175 SkArenaAlloc* alloc) const {
Brian Osman0e189372018-10-19 11:58:29 -0400176 if (fImage->alphaType() == kUnpremul_SkAlphaType) {
Florin Malitaaf2769d2018-04-04 13:46:35 -0400177 return nullptr;
178 }
Brian Osmanb70fd912018-10-22 16:10:44 -0400179 if (fImage->colorType() != kN32_SkColorType) {
180 return nullptr;
181 }
Florin Malitaaf2769d2018-04-04 13:46:35 -0400182 if (fTileModeX != fTileModeY) {
183 return nullptr;
184 }
Mike Reedfae8fce2019-04-03 10:27:45 -0400185 if (fTileModeX == SkTileMode::kDecal || fTileModeY == SkTileMode::kDecal) {
Florin Malitaaf2769d2018-04-04 13:46:35 -0400186 return nullptr;
187 }
188
Mike Reedbc4d88a2021-01-21 12:49:36 -0500189 auto supported = [](const SkSamplingOptions& sampling) {
190 const std::tuple<SkFilterMode,SkMipmapMode> supported[] = {
Mike Reed7cee3ef2021-03-19 16:00:52 -0400191 {SkFilterMode::kNearest, SkMipmapMode::kNone}, // legacy None
192 {SkFilterMode::kLinear, SkMipmapMode::kNone}, // legacy Low
193 {SkFilterMode::kLinear, SkMipmapMode::kNearest}, // legacy Medium
Mike Reedbc4d88a2021-01-21 12:49:36 -0500194 };
195 for (auto [f, m] : supported) {
196 if (sampling.filter == f && sampling.mipmap == m) {
197 return true;
198 }
Mike Reed74c51ac2020-11-20 10:23:58 -0500199 }
Mike Reedbc4d88a2021-01-21 12:49:36 -0500200 return false;
201 };
Mike Reed225cdea2021-03-18 22:19:44 -0400202 if (fSampling.useCubic || !supported(fSampling)) {
Mike Reed74c51ac2020-11-20 10:23:58 -0500203 return nullptr;
204 }
205
Mike Kleindac694d2018-12-18 10:13:52 -0500206 // SkBitmapProcShader stores bitmap coordinates in a 16bit buffer,
Mike Klein67761eb2018-12-18 10:16:53 -0500207 // so it can't handle bitmaps larger than 65535.
Mike Kleindac694d2018-12-18 10:13:52 -0500208 //
Mike Klein67761eb2018-12-18 10:16:53 -0500209 // We back off another bit to 32767 to make small amounts of
210 // intermediate math safe, e.g. in
211 //
212 // SkFixed fx = ...;
213 // fx = tile(fx + SK_Fixed1);
214 //
215 // we want to make sure (fx + SK_Fixed1) never overflows.
216 if (fImage-> width() > 32767 ||
217 fImage->height() > 32767) {
Mike Kleindac694d2018-12-18 10:13:52 -0500218 return nullptr;
219 }
220
Florin Malitaaf2769d2018-04-04 13:46:35 -0400221 SkMatrix inv;
222 if (!this->computeTotalInverse(*rec.fMatrix, rec.fLocalMatrix, &inv) ||
223 !legacy_shader_can_handle(inv)) {
224 return nullptr;
225 }
226
Mike Reed011d1662019-02-28 17:19:25 -0500227 if (!rec.isLegacyCompatible(fImage->colorSpace())) {
228 return nullptr;
229 }
230
Mike Reed225cdea2021-03-18 22:19:44 -0400231 return SkBitmapProcLegacyShader::MakeContext(*this, fTileModeX, fTileModeY, fSampling,
232 as_IB(fImage.get()), rec, alloc);
reed856e9d92015-09-30 12:21:45 -0700233}
Mike Reede92aae62018-10-17 10:21:51 -0400234#endif
reed856e9d92015-09-30 12:21:45 -0700235
Mike Reedfae8fce2019-04-03 10:27:45 -0400236SkImage* SkImageShader::onIsAImage(SkMatrix* texM, SkTileMode xy[]) const {
reedf1ac1822016-08-01 11:24:14 -0700237 if (texM) {
238 *texM = this->getLocalMatrix();
239 }
240 if (xy) {
Mike Reedfae8fce2019-04-03 10:27:45 -0400241 xy[0] = fTileModeX;
242 xy[1] = fTileModeY;
reedf1ac1822016-08-01 11:24:14 -0700243 }
244 return const_cast<SkImage*>(fImage.get());
245}
246
Mike Klein1f313092018-01-03 10:30:21 -0500247sk_sp<SkShader> SkImageShader::Make(sk_sp<SkImage> image,
Mike Reede25b4472019-04-02 17:49:12 -0400248 SkTileMode tmx, SkTileMode tmy,
Mike Reed225cdea2021-03-18 22:19:44 -0400249 const SkSamplingOptions& options,
Mike Klein1f313092018-01-03 10:30:21 -0500250 const SkMatrix* localMatrix,
251 bool clampAsIfUnpremul) {
Mike Reed15b95d62020-11-06 09:50:47 -0500252 auto is_unit = [](float x) {
253 return x >= 0 && x <= 1;
254 };
Mike Reed225cdea2021-03-18 22:19:44 -0400255 if (options.useCubic) {
256 if (!is_unit(options.cubic.B) || !is_unit(options.cubic.C)) {
Mike Reed15b95d62020-11-06 09:50:47 -0500257 return nullptr;
258 }
259 }
Mike Reedf8a6b5b2020-07-10 08:36:42 -0400260 if (!image) {
261 return sk_make_sp<SkEmptyShader>();
262 }
263 return sk_sp<SkShader>{
Mike Reed74c51ac2020-11-20 10:23:58 -0500264 new SkImageShader(image, tmx, tmy, options, localMatrix, clampAsIfUnpremul)
Mike Reedf8a6b5b2020-07-10 08:36:42 -0400265 };
266}
267
reed856e9d92015-09-30 12:21:45 -0700268///////////////////////////////////////////////////////////////////////////////////////////////////
269
270#if SK_SUPPORT_GPU
271
Brian Salomon4bc0c1f2019-09-30 15:12:27 -0400272#include "src/gpu/GrColorInfo.h"
John Stilesf743d4e2020-07-23 11:35:08 -0400273#include "src/gpu/effects/GrBlendFragmentProcessor.h"
reed856e9d92015-09-30 12:21:45 -0700274
Brian Salomonaff329b2017-08-11 09:40:37 -0400275std::unique_ptr<GrFragmentProcessor> SkImageShader::asFragmentProcessor(
Mike Reede3429e62018-01-19 11:43:34 -0500276 const GrFPArgs& args) const {
Florin Malita52f02912020-03-09 16:33:17 -0400277 const auto lm = this->totalLocalMatrix(args.fPreLocalMatrix);
reed856e9d92015-09-30 12:21:45 -0700278 SkMatrix lmInverse;
Florin Malitac6c5ead2018-04-11 15:33:40 -0400279 if (!lm->invert(&lmInverse)) {
reed856e9d92015-09-30 12:21:45 -0700280 return nullptr;
281 }
reed856e9d92015-09-30 12:21:45 -0700282
Brian Salomon6ef10e92021-04-20 09:11:24 -0400283 SkTileMode tileModes[2] = {fTileModeX, fTileModeY};
284 auto fp = as_IB(fImage.get())->asFragmentProcessor(args.fContext,
285 fSampling,
286 tileModes,
287 lmInverse);
288 if (!fp) {
289 return nullptr;
Brian Salomonf7353512020-07-22 19:26:48 -0400290 }
Brian Salomon490f1922021-02-01 13:14:37 -0500291
Brian Salomon490f1922021-02-01 13:14:37 -0500292 fp = GrColorSpaceXformEffect::Make(std::move(fp),
293 fImage->colorSpace(),
294 fImage->alphaType(),
295 args.fDstColorInfo->colorSpace(),
296 kPremul_SkAlphaType);
Brian Salomonb43d6992021-01-05 14:37:40 -0500297 if (fImage->isAlphaOnly()) {
Brian Salomon4b6e2f02021-07-08 14:04:21 -0400298 return GrBlendFragmentProcessor::Make(std::move(fp), nullptr, SkBlendMode::kDstIn);
Brian Salomonc0d79e52019-04-10 15:02:11 -0400299 } else if (args.fInputColorIsOpaque) {
Brian Osman55cbc752021-05-27 11:57:13 -0400300 // If the input alpha is known to be 1, we don't need to take the kSrcIn path. This is
301 // just an optimization. However, we can't just return 'fp' here. We need to actually
302 // inhibit the coverage-as-alpha optimization, or we'll fail to incorporate AA correctly.
303 // The OverrideInput FP happens to do that, so wrap our fp in one of those. The texture FP
304 // doesn't actually use the input color at all, so the overridden input is irrelevant.
Brian Salomon0c243202020-06-29 14:29:25 -0400305 return GrFragmentProcessor::OverrideInput(std::move(fp), SK_PMColor4fWHITE, false);
reed856e9d92015-09-30 12:21:45 -0700306 }
Brian Salomon4b6e2f02021-07-08 14:04:21 -0400307 return GrBlendFragmentProcessor::Make(std::move(fp), nullptr, SkBlendMode::kSrcIn);
reed856e9d92015-09-30 12:21:45 -0700308}
309
310#endif
reed320a40d2016-08-02 06:12:06 -0700311
312///////////////////////////////////////////////////////////////////////////////////////////////////
Mike Kleinc0bd9f92019-04-23 12:05:21 -0500313#include "src/core/SkImagePriv.h"
reed320a40d2016-08-02 06:12:06 -0700314
Michael Ludwigc47e81b2019-04-02 15:18:02 -0400315sk_sp<SkShader> SkMakeBitmapShaderForPaint(const SkPaint& paint, const SkBitmap& src,
Mike Reede25b4472019-04-02 17:49:12 -0400316 SkTileMode tmx, SkTileMode tmy,
Mike Reed172ba9e2020-12-17 15:57:55 -0500317 const SkSamplingOptions& sampling,
Michael Ludwigc47e81b2019-04-02 15:18:02 -0400318 const SkMatrix* localMatrix, SkCopyPixelsMode mode) {
Mike Reed172ba9e2020-12-17 15:57:55 -0500319 auto s = SkImageShader::Make(SkMakeImageFromRasterBitmap(src, mode),
Mike Reed225cdea2021-03-18 22:19:44 -0400320 tmx, tmy, sampling, localMatrix);
Michael Ludwigc47e81b2019-04-02 15:18:02 -0400321 if (!s) {
322 return nullptr;
323 }
324 if (src.colorType() == kAlpha_8_SkColorType && paint.getShader()) {
325 // Compose the image shader with the paint's shader. Alpha images+shaders should output the
326 // texture's alpha multiplied by the shader's color. DstIn (d*sa) will achieve this with
327 // the source image and dst shader (MakeBlend takes dst first, src second).
Mike Reedc8bea7d2019-04-09 13:55:36 -0400328 s = SkShaders::Blend(SkBlendMode::kDstIn, paint.refShader(), std::move(s));
Michael Ludwigc47e81b2019-04-02 15:18:02 -0400329 }
330 return s;
331}
332
Brian Salomon23356442018-11-30 15:33:19 -0500333void SkShaderBase::RegisterFlattenables() { SK_REGISTER_FLATTENABLE(SkImageShader); }
reed320a40d2016-08-02 06:12:06 -0700334
Mike Reed9318a6c2019-08-16 16:16:25 -0400335class SkImageStageUpdater : public SkStageUpdater {
336public:
Mike Reed8845c372019-12-19 13:22:08 -0500337 SkImageStageUpdater(const SkImageShader* shader, bool usePersp)
Mike Reed0ae1b3d2020-03-04 17:47:43 -0500338 : fShader(shader)
339 , fUsePersp(usePersp || as_SB(shader)->getLocalMatrix().hasPerspective())
Mike Reed8845c372019-12-19 13:22:08 -0500340 {}
Mike Reed9318a6c2019-08-16 16:16:25 -0400341
Mike Reed8845c372019-12-19 13:22:08 -0500342 const SkImageShader* fShader;
343 const bool fUsePersp; // else use affine
344
345 // large enough for perspective, though often we just use 2x3
346 float fMatrixStorage[9];
Mike Reed9318a6c2019-08-16 16:16:25 -0400347
348#if 0 // TODO: when we support mipmaps
349 SkRasterPipeline_GatherCtx* fGather;
350 SkRasterPipeline_TileCtx* fLimitX;
351 SkRasterPipeline_TileCtx* fLimitY;
352 SkRasterPipeline_DecalTileCtx* fDecal;
353#endif
354
Mike Reed8845c372019-12-19 13:22:08 -0500355 void append_matrix_stage(SkRasterPipeline* p) {
356 if (fUsePersp) {
357 p->append(SkRasterPipeline::matrix_perspective, fMatrixStorage);
358 } else {
359 p->append(SkRasterPipeline::matrix_2x3, fMatrixStorage);
360 }
361 }
362
Brian Osmand32ff902021-07-23 14:16:01 -0400363 bool update(const SkMatrix& ctm) override {
Mike Reed9318a6c2019-08-16 16:16:25 -0400364 SkMatrix matrix;
Brian Osmand32ff902021-07-23 14:16:01 -0400365 // TODO: We may have lost the original local matrix?
366 if (fShader->computeTotalInverse(ctm, nullptr, &matrix)) {
Mike Reed8845c372019-12-19 13:22:08 -0500367 if (fUsePersp) {
368 matrix.get9(fMatrixStorage);
369 } else {
Mike Reed0ae1b3d2020-03-04 17:47:43 -0500370 // if we get here, matrix should be affine. If it isn't, then defensively we
371 // won't draw (by returning false), but we should work to never let this
372 // happen (i.e. better preflight by the caller to know ahead of time that we
373 // may encounter perspective, either in the CTM, or in the localM).
374 //
375 // See https://bugs.chromium.org/p/skia/issues/detail?id=10004
376 //
377 if (!matrix.asAffine(fMatrixStorage)) {
378 SkASSERT(false);
379 return false;
380 }
Mike Reed8845c372019-12-19 13:22:08 -0500381 }
382 return true;
383 }
384 return false;
Mike Reed9318a6c2019-08-16 16:16:25 -0400385 }
386};
387
Mike Klein71d420d2021-02-03 09:54:07 -0600388static SkSamplingOptions tweak_sampling(SkSamplingOptions sampling, const SkMatrix& matrix) {
Mike Kleinbb1933e2020-12-02 15:45:29 -0600389 SkFilterMode filter = sampling.filter;
Mike Kleindcc89602020-12-02 15:06:15 -0600390
Mike Klein6dbd7ff2020-01-06 11:50:37 -0600391 // When the matrix is just an integer translate, bilerp == nearest neighbor.
Mike Kleindcc89602020-12-02 15:06:15 -0600392 if (filter == SkFilterMode::kLinear &&
Mike Klein71d420d2021-02-03 09:54:07 -0600393 matrix.getType() <= SkMatrix::kTranslate_Mask &&
394 matrix.getTranslateX() == (int)matrix.getTranslateX() &&
395 matrix.getTranslateY() == (int)matrix.getTranslateY()) {
Mike Kleindcc89602020-12-02 15:06:15 -0600396 filter = SkFilterMode::kNearest;
Mike Klein6dbd7ff2020-01-06 11:50:37 -0600397 }
398
Mike Klein71d420d2021-02-03 09:54:07 -0600399 return SkSamplingOptions(filter, sampling.mipmap);
400}
401
402static SkMatrix tweak_inv_matrix(SkFilterMode filter, SkMatrix matrix) {
Mike Klein6dbd7ff2020-01-06 11:50:37 -0600403 // See skia:4649 and the GM image_scale_aligned.
Mike Kleindcc89602020-12-02 15:06:15 -0600404 if (filter == SkFilterMode::kNearest) {
Mike Klein71d420d2021-02-03 09:54:07 -0600405 if (matrix.getScaleX() >= 0) {
406 matrix.setTranslateX(nextafterf(matrix.getTranslateX(),
407 floorf(matrix.getTranslateX())));
Mike Klein6dbd7ff2020-01-06 11:50:37 -0600408 }
Mike Klein71d420d2021-02-03 09:54:07 -0600409 if (matrix.getScaleY() >= 0) {
410 matrix.setTranslateY(nextafterf(matrix.getTranslateY(),
411 floorf(matrix.getTranslateY())));
Mike Klein6dbd7ff2020-01-06 11:50:37 -0600412 }
413 }
Mike Klein71d420d2021-02-03 09:54:07 -0600414 return matrix;
Mike Klein6dbd7ff2020-01-06 11:50:37 -0600415}
416
Mike Reed9318a6c2019-08-16 16:16:25 -0400417bool SkImageShader::doStages(const SkStageRec& rec, SkImageStageUpdater* updater) const {
Mike Reed39b4c862020-11-25 16:15:53 -0500418 // We only support certain sampling options in stages so far
Mike Reed225cdea2021-03-18 22:19:44 -0400419 auto sampling = fSampling;
Mike Kleinbb1933e2020-12-02 15:45:29 -0600420 if (sampling.useCubic) {
421 if (!is_default_cubic_resampler(sampling.cubic)) {
Mike Reed39b4c862020-11-25 16:15:53 -0500422 return false;
Mike Reed74c51ac2020-11-20 10:23:58 -0500423 }
Mike Kleinbb1933e2020-12-02 15:45:29 -0600424 } else if (sampling.mipmap == SkMipmapMode::kLinear) {
Mike Reed39b4c862020-11-25 16:15:53 -0500425 return false;
Mike Reedf8a6b5b2020-07-10 08:36:42 -0400426 }
Mike Reed9290d012020-06-11 16:56:06 -0400427
Mike Reed39b4c862020-11-25 16:15:53 -0500428
Mike Kleinbb1933e2020-12-02 15:45:29 -0600429 if (updater && (sampling.mipmap != SkMipmapMode::kNone)) {
Mike Reed8845c372019-12-19 13:22:08 -0500430 // TODO: medium: recall RequestBitmap and update width/height accordingly
Mike Reed9318a6c2019-08-16 16:16:25 -0400431 return false;
432 }
433
Mike Reed1d8c42e2017-08-29 14:58:19 -0400434 SkRasterPipeline* p = rec.fPipeline;
435 SkArenaAlloc* alloc = rec.fAlloc;
436
Florin Malita7558e4d2018-02-07 10:05:53 -0500437 SkMatrix matrix;
Brian Osman9aaec362020-05-08 14:54:37 -0400438 if (!this->computeTotalInverse(rec.fMatrixProvider.localToDevice(), rec.fLocalM, &matrix)) {
Mike Klein06a65e22016-11-17 12:39:09 -0500439 return false;
440 }
Florin Malitac7d6e0b2021-02-04 17:46:25 -0500441 matrix.normalizePerspective();
Mike Klein06a65e22016-11-17 12:39:09 -0500442
Mike Reed6e734042021-02-12 15:30:19 -0500443 SkASSERT(!sampling.useCubic || sampling.mipmap == SkMipmapMode::kNone);
Mike Reedbccdd902020-12-07 12:36:02 -0500444 auto* access = SkMipmapAccessor::Make(alloc, fImage.get(), matrix, sampling.mipmap);
445 if (!access) {
446 return false;
447 }
Mike Reed8c1ad7e2020-12-02 20:41:52 -0500448 SkPixmap pm;
449 std::tie(pm, matrix) = access->level();
Mike Kleinf447dee2016-11-29 16:37:12 -0500450
Mike Kleine8de0242018-03-10 12:37:11 -0500451 p->append(SkRasterPipeline::seed_shader);
Mike Reed9318a6c2019-08-16 16:16:25 -0400452
453 if (updater) {
Mike Reed8845c372019-12-19 13:22:08 -0500454 updater->append_matrix_stage(p);
Mike Reed9318a6c2019-08-16 16:16:25 -0400455 } else {
Mike Kleinbb1933e2020-12-02 15:45:29 -0600456 if (!sampling.useCubic) {
Mike Klein71d420d2021-02-03 09:54:07 -0600457 // TODO: can tweak_sampling sometimes for cubic too when B=0
458 if (rec.fMatrixProvider.localToDeviceHitsPixelCenters()) {
459 sampling = tweak_sampling(sampling, matrix);
460 }
461 matrix = tweak_inv_matrix(sampling.filter, matrix);
Mike Reed3d58d5a2020-11-23 13:32:36 -0500462 }
Mike Reed9318a6c2019-08-16 16:16:25 -0400463 p->append_matrix(alloc, matrix);
464 }
Mike Klein06a65e22016-11-17 12:39:09 -0500465
Mike Kleinb11ab572018-10-24 06:42:14 -0400466 auto gather = alloc->make<SkRasterPipeline_GatherCtx>();
Mike Klein1fa9c432017-12-11 09:59:47 -0500467 gather->pixels = pm.addr();
Mike Klein968af432017-07-18 16:31:55 -0400468 gather->stride = pm.rowBytesAsPixels();
Mike Kleinf3b4e162017-09-22 15:32:59 -0400469 gather->width = pm.width();
470 gather->height = pm.height();
Mike Klein0a904492017-04-12 12:52:48 -0400471
Mike Kleinb11ab572018-10-24 06:42:14 -0400472 auto limit_x = alloc->make<SkRasterPipeline_TileCtx>(),
473 limit_y = alloc->make<SkRasterPipeline_TileCtx>();
Mike Reed51e46d52017-06-23 14:21:25 -0400474 limit_x->scale = pm.width();
475 limit_x->invScale = 1.0f / pm.width();
476 limit_y->scale = pm.height();
477 limit_y->invScale = 1.0f / pm.height();
Mike Kleinfc84dc52017-05-11 15:29:31 -0400478
Mike Kleinb11ab572018-10-24 06:42:14 -0400479 SkRasterPipeline_DecalTileCtx* decal_ctx = nullptr;
Mike Reedfae8fce2019-04-03 10:27:45 -0400480 bool decal_x_and_y = fTileModeX == SkTileMode::kDecal && fTileModeY == SkTileMode::kDecal;
481 if (fTileModeX == SkTileMode::kDecal || fTileModeY == SkTileMode::kDecal) {
Mike Kleinb11ab572018-10-24 06:42:14 -0400482 decal_ctx = alloc->make<SkRasterPipeline_DecalTileCtx>();
Mike Reeddfc0e912018-02-16 12:40:18 -0500483 decal_ctx->limit_x = limit_x->scale;
484 decal_ctx->limit_y = limit_y->scale;
485 }
486
Mike Kleinb04c3522016-11-28 11:55:58 -0500487 auto append_tiling_and_gather = [&] {
Mike Reeddfc0e912018-02-16 12:40:18 -0500488 if (decal_x_and_y) {
489 p->append(SkRasterPipeline::decal_x_and_y, decal_ctx);
490 } else {
491 switch (fTileModeX) {
Mike Reedfae8fce2019-04-03 10:27:45 -0400492 case SkTileMode::kClamp: /* The gather_xxx stage will clamp for us. */ break;
493 case SkTileMode::kMirror: p->append(SkRasterPipeline::mirror_x, limit_x); break;
494 case SkTileMode::kRepeat: p->append(SkRasterPipeline::repeat_x, limit_x); break;
495 case SkTileMode::kDecal: p->append(SkRasterPipeline::decal_x, decal_ctx); break;
Mike Reeddfc0e912018-02-16 12:40:18 -0500496 }
497 switch (fTileModeY) {
Mike Reedfae8fce2019-04-03 10:27:45 -0400498 case SkTileMode::kClamp: /* The gather_xxx stage will clamp for us. */ break;
499 case SkTileMode::kMirror: p->append(SkRasterPipeline::mirror_y, limit_y); break;
500 case SkTileMode::kRepeat: p->append(SkRasterPipeline::repeat_y, limit_y); break;
501 case SkTileMode::kDecal: p->append(SkRasterPipeline::decal_y, decal_ctx); break;
Mike Reeddfc0e912018-02-16 12:40:18 -0500502 }
Mike Kleinf7f883b2016-11-21 15:09:45 -0500503 }
Mike Reeddfc0e912018-02-16 12:40:18 -0500504
Mike Kleinac568a92018-01-25 09:09:32 -0500505 void* ctx = gather;
Mike Reed8c1ad7e2020-12-02 20:41:52 -0500506 switch (pm.colorType()) {
Mike Kleinac568a92018-01-25 09:09:32 -0500507 case kAlpha_8_SkColorType: p->append(SkRasterPipeline::gather_a8, ctx); break;
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400508 case kA16_unorm_SkColorType: p->append(SkRasterPipeline::gather_a16, ctx); break;
509 case kA16_float_SkColorType: p->append(SkRasterPipeline::gather_af16, ctx); break;
Mike Kleinac568a92018-01-25 09:09:32 -0500510 case kRGB_565_SkColorType: p->append(SkRasterPipeline::gather_565, ctx); break;
511 case kARGB_4444_SkColorType: p->append(SkRasterPipeline::gather_4444, ctx); break;
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400512 case kR8G8_unorm_SkColorType: p->append(SkRasterPipeline::gather_rg88, ctx); break;
513 case kR16G16_unorm_SkColorType: p->append(SkRasterPipeline::gather_rg1616, ctx); break;
514 case kR16G16_float_SkColorType: p->append(SkRasterPipeline::gather_rgf16, ctx); break;
Mike Kleinac568a92018-01-25 09:09:32 -0500515 case kRGBA_8888_SkColorType: p->append(SkRasterPipeline::gather_8888, ctx); break;
516 case kRGBA_1010102_SkColorType: p->append(SkRasterPipeline::gather_1010102, ctx); break;
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400517 case kR16G16B16A16_unorm_SkColorType:
518 p->append(SkRasterPipeline::gather_16161616,ctx); break;
Mike Kleinb70990e2019-02-28 10:03:27 -0600519 case kRGBA_F16Norm_SkColorType:
Mike Kleinac568a92018-01-25 09:09:32 -0500520 case kRGBA_F16_SkColorType: p->append(SkRasterPipeline::gather_f16, ctx); break;
Mike Klein37854712018-06-26 11:43:06 -0400521 case kRGBA_F32_SkColorType: p->append(SkRasterPipeline::gather_f32, ctx); break;
Mike Kleinac568a92018-01-25 09:09:32 -0500522
Mike Kleinb1df5e52018-10-17 17:06:03 -0400523 case kGray_8_SkColorType: p->append(SkRasterPipeline::gather_a8, ctx);
524 p->append(SkRasterPipeline::alpha_to_gray ); break;
Mike Klein1a3eb522018-10-18 10:11:00 -0400525
Mike Kleinac568a92018-01-25 09:09:32 -0500526 case kRGB_888x_SkColorType: p->append(SkRasterPipeline::gather_8888, ctx);
527 p->append(SkRasterPipeline::force_opaque ); break;
Mike Klein1a3eb522018-10-18 10:11:00 -0400528
Mike Kleinf7eb0542020-02-11 12:19:08 -0600529 case kBGRA_1010102_SkColorType: p->append(SkRasterPipeline::gather_1010102, ctx);
530 p->append(SkRasterPipeline::swap_rb ); break;
531
Mike Kleinac568a92018-01-25 09:09:32 -0500532 case kRGB_101010x_SkColorType: p->append(SkRasterPipeline::gather_1010102, ctx);
533 p->append(SkRasterPipeline::force_opaque ); break;
534
Mike Kleinf7eb0542020-02-11 12:19:08 -0600535 case kBGR_101010x_SkColorType: p->append(SkRasterPipeline::gather_1010102, ctx);
536 p->append(SkRasterPipeline::force_opaque );
537 p->append(SkRasterPipeline::swap_rb ); break;
538
Mike Klein1a3eb522018-10-18 10:11:00 -0400539 case kBGRA_8888_SkColorType: p->append(SkRasterPipeline::gather_8888, ctx);
540 p->append(SkRasterPipeline::swap_rb ); break;
541
Mike Kleinb70990e2019-02-28 10:03:27 -0600542 case kUnknown_SkColorType: SkASSERT(false);
Mike Kleinb04c3522016-11-28 11:55:58 -0500543 }
Mike Reeddfc0e912018-02-16 12:40:18 -0500544 if (decal_ctx) {
545 p->append(SkRasterPipeline::check_decal_mask, decal_ctx);
546 }
Mike Kleinf7f883b2016-11-21 15:09:45 -0500547 };
548
Mike Klein1fa9c432017-12-11 09:59:47 -0500549 auto append_misc = [&] {
Mike Reed8c1ad7e2020-12-02 20:41:52 -0500550 SkColorSpace* cs = pm.colorSpace();
551 SkAlphaType at = pm.alphaType();
Mike Klein6a9f1c42020-01-02 23:23:42 -0600552
553 // Color for A8 images comes from the paint. TODO: all alpha images? none?
Mike Reed8c1ad7e2020-12-02 20:41:52 -0500554 if (pm.colorType() == kAlpha_8_SkColorType) {
Mike Klein6a9f1c42020-01-02 23:23:42 -0600555 SkColor4f rgb = rec.fPaint.getColor4f();
556 p->append_set_rgb(alloc, rgb);
557
Mike Klein6a9f1c42020-01-02 23:23:42 -0600558 cs = sk_srgb_singleton();
559 at = kUnpremul_SkAlphaType;
560 }
561
Mike Klein059e0432020-01-02 16:32:38 -0600562 // Bicubic filtering naturally produces out of range values on both sides of [0,1].
Mike Kleinbb1933e2020-12-02 15:45:29 -0600563 if (sampling.useCubic) {
Mike Klein1fa9c432017-12-11 09:59:47 -0500564 p->append(SkRasterPipeline::clamp_0);
Mike Klein059e0432020-01-02 16:32:38 -0600565 p->append(at == kUnpremul_SkAlphaType || fClampAsIfUnpremul
566 ? SkRasterPipeline::clamp_1
567 : SkRasterPipeline::clamp_a);
Mike Klein1fa9c432017-12-11 09:59:47 -0500568 }
Mike Kleinb82edcc2018-07-10 18:25:03 +0000569
Mike Klein059e0432020-01-02 16:32:38 -0600570 // Transform color space and alpha type to match shader convention (dst CS, premul alpha).
571 alloc->make<SkColorSpaceXformSteps>(cs, at,
572 rec.fDstCS, kPremul_SkAlphaType)
Mike Kleinec8e0bf2020-05-22 11:42:38 -0500573 ->apply(p);
Mike Kleinb82edcc2018-07-10 18:25:03 +0000574
Mike Klein1fa9c432017-12-11 09:59:47 -0500575 return true;
576 };
577
Mike Reed9318a6c2019-08-16 16:16:25 -0400578 // Check for fast-path stages.
Mike Reed8c1ad7e2020-12-02 20:41:52 -0500579 auto ct = pm.colorType();
Mike Klein8e3426f2018-04-16 12:56:24 -0400580 if (true
581 && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType)
Mike Kleinbb1933e2020-12-02 15:45:29 -0600582 && !sampling.useCubic && sampling.filter == SkFilterMode::kLinear
Mike Reedfae8fce2019-04-03 10:27:45 -0400583 && fTileModeX == SkTileMode::kClamp && fTileModeY == SkTileMode::kClamp) {
Mike Klein1fa9c432017-12-11 09:59:47 -0500584
585 p->append(SkRasterPipeline::bilerp_clamp_8888, gather);
Mike Klein8e3426f2018-04-16 12:56:24 -0400586 if (ct == kBGRA_8888_SkColorType) {
587 p->append(SkRasterPipeline::swap_rb);
588 }
Mike Klein1fa9c432017-12-11 09:59:47 -0500589 return append_misc();
590 }
Mike Reed78eedba2019-07-31 16:39:15 -0400591 if (true
Mike Klein01005622019-08-13 12:22:17 -0400592 && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType) // TODO: all formats
Mike Kleinbb1933e2020-12-02 15:45:29 -0600593 && !sampling.useCubic && sampling.filter == SkFilterMode::kLinear
Mike Klein01005622019-08-13 12:22:17 -0400594 && fTileModeX != SkTileMode::kDecal // TODO decal too?
595 && fTileModeY != SkTileMode::kDecal) {
596
597 auto ctx = alloc->make<SkRasterPipeline_SamplerCtx2>();
598 *(SkRasterPipeline_GatherCtx*)(ctx) = *gather;
599 ctx->ct = ct;
600 ctx->tileX = fTileModeX;
601 ctx->tileY = fTileModeY;
602 ctx->invWidth = 1.0f / ctx->width;
603 ctx->invHeight = 1.0f / ctx->height;
604 p->append(SkRasterPipeline::bilinear, ctx);
605 return append_misc();
606 }
607 if (true
Mike Reed78eedba2019-07-31 16:39:15 -0400608 && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType)
Mike Kleinbb1933e2020-12-02 15:45:29 -0600609 && sampling.useCubic
Mike Reed78eedba2019-07-31 16:39:15 -0400610 && fTileModeX == SkTileMode::kClamp && fTileModeY == SkTileMode::kClamp) {
611
612 p->append(SkRasterPipeline::bicubic_clamp_8888, gather);
613 if (ct == kBGRA_8888_SkColorType) {
614 p->append(SkRasterPipeline::swap_rb);
615 }
616 return append_misc();
617 }
Mike Klein01005622019-08-13 12:22:17 -0400618 if (true
619 && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType) // TODO: all formats
Mike Kleinbb1933e2020-12-02 15:45:29 -0600620 && sampling.useCubic
Mike Klein01005622019-08-13 12:22:17 -0400621 && fTileModeX != SkTileMode::kDecal // TODO decal too?
622 && fTileModeY != SkTileMode::kDecal) {
623
624 auto ctx = alloc->make<SkRasterPipeline_SamplerCtx2>();
625 *(SkRasterPipeline_GatherCtx*)(ctx) = *gather;
626 ctx->ct = ct;
627 ctx->tileX = fTileModeX;
628 ctx->tileY = fTileModeY;
629 ctx->invWidth = 1.0f / ctx->width;
630 ctx->invHeight = 1.0f / ctx->height;
631 p->append(SkRasterPipeline::bicubic, ctx);
632 return append_misc();
633 }
Mike Klein1fa9c432017-12-11 09:59:47 -0500634
Mike Reed3d58d5a2020-11-23 13:32:36 -0500635 SkRasterPipeline_SamplerCtx* sampler = alloc->make<SkRasterPipeline_SamplerCtx>();
Mike Klein0a904492017-04-12 12:52:48 -0400636
Mike Kleinb0b17d12016-12-09 16:25:44 -0500637 auto sample = [&](SkRasterPipeline::StockStage setup_x,
638 SkRasterPipeline::StockStage setup_y) {
Mike Klein0a904492017-04-12 12:52:48 -0400639 p->append(setup_x, sampler);
640 p->append(setup_y, sampler);
Mike Klein886cf532016-12-06 11:31:25 -0500641 append_tiling_and_gather();
Mike Klein0a904492017-04-12 12:52:48 -0400642 p->append(SkRasterPipeline::accumulate, sampler);
Mike Klein886cf532016-12-06 11:31:25 -0500643 };
644
Mike Kleinbb1933e2020-12-02 15:45:29 -0600645 if (sampling.useCubic) {
Mike Klein0a904492017-04-12 12:52:48 -0400646 p->append(SkRasterPipeline::save_xy, sampler);
Mike Kleinb0b17d12016-12-09 16:25:44 -0500647
648 sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n3y);
649 sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n3y);
650 sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n3y);
651 sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n3y);
652
653 sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n1y);
654 sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n1y);
655 sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n1y);
656 sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n1y);
657
658 sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p1y);
659 sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p1y);
660 sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p1y);
661 sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p1y);
662
663 sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p3y);
664 sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p3y);
665 sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p3y);
666 sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p3y);
667
Mike Kleinb04c3522016-11-28 11:55:58 -0500668 p->append(SkRasterPipeline::move_dst_src);
Mike Kleinbb1933e2020-12-02 15:45:29 -0600669 } else if (sampling.filter == SkFilterMode::kLinear) {
Mike Reed3d58d5a2020-11-23 13:32:36 -0500670 p->append(SkRasterPipeline::save_xy, sampler);
671
672 sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_ny);
673 sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_ny);
674 sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_py);
675 sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_py);
676
677 p->append(SkRasterPipeline::move_dst_src);
678 } else {
679 append_tiling_and_gather();
Mike Klein06a65e22016-11-17 12:39:09 -0500680 }
681
Mike Klein1fa9c432017-12-11 09:59:47 -0500682 return append_misc();
Mike Klein06a65e22016-11-17 12:39:09 -0500683}
Mike Reed9318a6c2019-08-16 16:16:25 -0400684
685bool SkImageShader::onAppendStages(const SkStageRec& rec) const {
686 return this->doStages(rec, nullptr);
687}
688
689SkStageUpdater* SkImageShader::onAppendUpdatableStages(const SkStageRec& rec) const {
Brian Osman9aaec362020-05-08 14:54:37 -0400690 bool usePersp = rec.fMatrixProvider.localToDevice().hasPerspective();
Mike Reed8845c372019-12-19 13:22:08 -0500691 auto updater = rec.fAlloc->make<SkImageStageUpdater>(this, usePersp);
Mike Reed9318a6c2019-08-16 16:16:25 -0400692 return this->doStages(rec, updater) ? updater : nullptr;
693}
694
Herb Derby5992f9e2021-07-17 18:19:03 -0400695class SkImageShader::TransformShader : public SkTransformShader {
696public:
697 explicit TransformShader(const SkImageShader& shader)
698 : SkTransformShader{shader}
699 , fImageShader{shader} {}
700
701 skvm::Color onProgram(skvm::Builder* b,
702 skvm::Coord device, skvm::Coord local, skvm::Color color,
703 const SkMatrixProvider& matrices, const SkMatrix* localM,
704 const SkColorInfo& dst,
705 skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override {
706 return fImageShader.makeProgram(
707 b, device, local, color, matrices, localM, dst, uniforms, this, alloc);
708 }
709
710private:
711 const SkImageShader& fImageShader;
712};
713
714SkUpdatableShader* SkImageShader::onUpdatableShader(SkArenaAlloc* alloc) const {
715 return alloc->make<TransformShader>(*this);
716}
717
718skvm::Color SkImageShader::onProgram(skvm::Builder* b,
Mike Reedf8a6b5b2020-07-10 08:36:42 -0400719 skvm::Coord device, skvm::Coord origLocal, skvm::Color paint,
Mike Kleine81b1082020-06-19 11:29:13 -0500720 const SkMatrixProvider& matrices, const SkMatrix* localM,
Mike Reed80468372021-03-19 14:05:19 -0400721 const SkColorInfo& dst,
Mike Klein276a7852020-03-15 08:46:09 -0500722 skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
Herb Derby5992f9e2021-07-17 18:19:03 -0400723 return this->makeProgram(
724 b, device, origLocal, paint, matrices, localM, dst, uniforms, nullptr, alloc);
725}
726
727skvm::Color SkImageShader::makeProgram(
728 skvm::Builder* p, skvm::Coord device, skvm::Coord origLocal, skvm::Color paint,
729 const SkMatrixProvider& matrices, const SkMatrix* localM, const SkColorInfo& dst,
730 skvm::Uniforms* uniforms, const TransformShader* coordShader, SkArenaAlloc* alloc) const {
731
Mike Reedf8a6b5b2020-07-10 08:36:42 -0400732 SkMatrix baseInv;
733 if (!this->computeTotalInverse(matrices.localToDevice(), localM, &baseInv)) {
Mike Reed6352f002020-03-14 23:30:10 -0400734 return {};
Mike Kleinf6a715b2019-12-30 15:24:18 -0600735 }
Mike Reedf8a6b5b2020-07-10 08:36:42 -0400736 baseInv.normalizePerspective();
Mike Kleinf6a715b2019-12-30 15:24:18 -0600737
Mike Reed225cdea2021-03-18 22:19:44 -0400738 auto sampling = fSampling;
Mike Reedbccdd902020-12-07 12:36:02 -0500739 auto* access = SkMipmapAccessor::Make(alloc, fImage.get(), baseInv, sampling.mipmap);
740 if (!access) {
741 return {};
742 }
Mike Reed8c1ad7e2020-12-02 20:41:52 -0500743 auto [upper, upperInv] = access->level();
Herb Derby98c2dd32021-07-29 17:48:54 -0400744 // If we are using a coordShader, then we can't make guesses about the state of the matrix.
745 if (!sampling.useCubic && !coordShader) {
Mike Klein71d420d2021-02-03 09:54:07 -0600746 // TODO: can tweak_sampling sometimes for cubic too when B=0
747 if (matrices.localToDeviceHitsPixelCenters()) {
748 sampling = tweak_sampling(sampling, upperInv);
749 }
750 upperInv = tweak_inv_matrix(sampling.filter, upperInv);
Mike Kleinf6a715b2019-12-30 15:24:18 -0600751 }
Mike Kleinf6a715b2019-12-30 15:24:18 -0600752
Mike Reed8c1ad7e2020-12-02 20:41:52 -0500753 SkPixmap lowerPixmap;
754 SkMatrix lowerInv;
755 SkPixmap* lower = nullptr;
756 float lowerWeight = access->lowerWeight();
757 if (lowerWeight > 0) {
758 std::tie(lowerPixmap, lowerInv) = access->lowerLevel();
759 lower = &lowerPixmap;
760 }
761
Herb Derby5992f9e2021-07-17 18:19:03 -0400762 skvm::Coord upperLocal;
763 if (coordShader != nullptr) {
764 upperLocal = coordShader->applyMatrix(p, upperInv, origLocal, uniforms);
765 } else {
766 upperLocal = SkShaderBase::ApplyMatrix(p, upperInv, origLocal, uniforms);
767 }
Mike Klein6dbd7ff2020-01-06 11:50:37 -0600768
Mike Klein921236b2020-02-13 11:20:54 -0600769 // We can exploit image opacity to skip work unpacking alpha channels.
Mike Reed8c1ad7e2020-12-02 20:41:52 -0500770 const bool input_is_opaque = SkAlphaTypeIsOpaque(upper.alphaType())
771 || SkColorTypeIsAlwaysOpaque(upper.colorType());
Mike Klein921236b2020-02-13 11:20:54 -0600772
Mike Klein03d89ef2020-01-14 17:18:29 -0600773 // Each call to sample() will try to rewrite the same uniforms over and over,
774 // so remember where we start and reset back there each time. That way each
775 // sample() call uses the same uniform offsets.
Mike Klein03d89ef2020-01-14 17:18:29 -0600776
Mike Reedf8a6b5b2020-07-10 08:36:42 -0400777 auto compute_clamp_limit = [&](float limit) {
778 // Subtract an ulp so the upper clamp limit excludes limit itself.
779 int bits;
780 memcpy(&bits, &limit, 4);
781 return p->uniformF(uniforms->push(bits-1));
782 };
Mike Klein03d89ef2020-01-14 17:18:29 -0600783
Mike Reedf8a6b5b2020-07-10 08:36:42 -0400784 // Except in the simplest case (no mips, no filtering), we reference uniforms
785 // more than once. To avoid adding/registering them multiple times, we pre-load them
786 // into a struct (just to logically group them together), based on the "current"
787 // pixmap (level of a mipmap).
788 //
789 struct Uniforms {
790 skvm::F32 w, iw, i2w,
791 h, ih, i2h;
792
793 skvm::F32 clamp_w,
794 clamp_h;
795
796 skvm::Uniform addr;
797 skvm::I32 rowBytesAsPixels;
798
Mike Klein03c932c2020-07-13 09:07:42 -0500799 skvm::PixelFormat pixelFormat; // not a uniform, but needed for each texel sample,
800 // so we store it here, since it is also dependent on
801 // the current pixmap (level).
Mike Reedf8a6b5b2020-07-10 08:36:42 -0400802 };
803
804 auto setup_uniforms = [&](const SkPixmap& pm) -> Uniforms {
Mike Klein447f3312021-02-08 09:46:59 -0600805 skvm::PixelFormat pixelFormat = skvm::SkColorType_to_PixelFormat(pm.colorType());
Mike Reedf8a6b5b2020-07-10 08:36:42 -0400806 return {
807 p->uniformF(uniforms->pushF( pm.width())),
808 p->uniformF(uniforms->pushF(1.0f/pm.width())), // iff tileX == kRepeat
809 p->uniformF(uniforms->pushF(0.5f/pm.width())), // iff tileX == kMirror
810
811 p->uniformF(uniforms->pushF( pm.height())),
812 p->uniformF(uniforms->pushF(1.0f/pm.height())), // iff tileY == kRepeat
813 p->uniformF(uniforms->pushF(0.5f/pm.height())), // iff tileY == kMirror
814
815 compute_clamp_limit(pm. width()),
816 compute_clamp_limit(pm.height()),
817
818 uniforms->pushPtr(pm.addr()),
819 p->uniform32(uniforms->push(pm.rowBytesAsPixels())),
820
Mike Klein03c932c2020-07-13 09:07:42 -0500821 pixelFormat,
Mike Reedf8a6b5b2020-07-10 08:36:42 -0400822 };
823 };
824
825 auto sample_texel = [&](const Uniforms& u, skvm::F32 sx, skvm::F32 sy) -> skvm::Color {
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600826 // repeat() and mirror() are written assuming they'll be followed by a [0,scale) clamp.
Mike Reedf8a6b5b2020-07-10 08:36:42 -0400827 auto repeat = [&](skvm::F32 v, skvm::F32 S, skvm::F32 I) {
Mike Reedf3b9a302020-04-01 13:18:02 -0400828 return v - floor(v * I) * S;
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600829 };
Mike Reedf8a6b5b2020-07-10 08:36:42 -0400830 auto mirror = [&](skvm::F32 v, skvm::F32 S, skvm::F32 I2) {
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600831 // abs( (v-scale) - (2*scale)*floor((v-scale)*(0.5f/scale)) - scale )
832 // {---A---} {------------------B------------------}
Mike Reedf3b9a302020-04-01 13:18:02 -0400833 skvm::F32 A = v - S,
834 B = (S + S) * floor(A * I2);
835 return abs(A - B - S);
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600836 };
837 switch (fTileModeX) {
838 case SkTileMode::kDecal: /* handled after gather */ break;
839 case SkTileMode::kClamp: /* we always clamp */ break;
Mike Reedf8a6b5b2020-07-10 08:36:42 -0400840 case SkTileMode::kRepeat: sx = repeat(sx, u.w, u.iw); break;
841 case SkTileMode::kMirror: sx = mirror(sx, u.w, u.i2w); break;
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600842 }
843 switch (fTileModeY) {
844 case SkTileMode::kDecal: /* handled after gather */ break;
845 case SkTileMode::kClamp: /* we always clamp */ break;
Mike Reedf8a6b5b2020-07-10 08:36:42 -0400846 case SkTileMode::kRepeat: sy = repeat(sy, u.h, u.ih); break;
847 case SkTileMode::kMirror: sy = mirror(sy, u.h, u.i2h); break;
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600848 }
849
850 // Always clamp sample coordinates to [0,width), [0,height), both for memory
851 // safety and to handle the clamps still needed by kClamp, kRepeat, and kMirror.
Mike Reedf8a6b5b2020-07-10 08:36:42 -0400852 skvm::F32 clamped_x = clamp(sx, 0, u.clamp_w),
853 clamped_y = clamp(sy, 0, u.clamp_h);
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600854
855 // Load pixels from pm.addr()[(int)sx + (int)sy*stride].
Mike Reedf3b9a302020-04-01 13:18:02 -0400856 skvm::I32 index = trunc(clamped_x) +
Mike Reedf8a6b5b2020-07-10 08:36:42 -0400857 trunc(clamped_y) * u.rowBytesAsPixels;
Mike Klein03c932c2020-07-13 09:07:42 -0500858 skvm::Color c = gather(u.pixelFormat, u.addr, index);
Mike Klein913a6f52020-03-23 11:06:25 -0500859
Mike Klein921236b2020-02-13 11:20:54 -0600860 // If we know the image is opaque, jump right to alpha = 1.0f, skipping work to unpack it.
861 if (input_is_opaque) {
Mike Klein46874ef2020-02-13 10:17:08 -0600862 c.a = p->splat(1.0f);
863 }
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600864
865 // Mask away any pixels that we tried to sample outside the bounds in kDecal.
866 if (fTileModeX == SkTileMode::kDecal || fTileModeY == SkTileMode::kDecal) {
867 skvm::I32 mask = p->splat(~0);
Mike Reedf3b9a302020-04-01 13:18:02 -0400868 if (fTileModeX == SkTileMode::kDecal) { mask &= (sx == clamped_x); }
869 if (fTileModeY == SkTileMode::kDecal) { mask &= (sy == clamped_y); }
Mike Klein5ec9c4e2020-12-01 10:43:46 -0600870 c.r = pun_to_F32(p->bit_and(mask, pun_to_I32(c.r)));
871 c.g = pun_to_F32(p->bit_and(mask, pun_to_I32(c.g)));
872 c.b = pun_to_F32(p->bit_and(mask, pun_to_I32(c.b)));
873 c.a = pun_to_F32(p->bit_and(mask, pun_to_I32(c.a)));
Mike Klein921236b2020-02-13 11:20:54 -0600874 // Notice that even if input_is_opaque, c.a might now be 0.
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600875 }
876
877 return c;
878 };
879
Mike Reedf8a6b5b2020-07-10 08:36:42 -0400880 auto sample_level = [&](const SkPixmap& pm, const SkMatrix& inv, skvm::Coord local) {
881 const Uniforms u = setup_uniforms(pm);
Mike Reed6352f002020-03-14 23:30:10 -0400882
Mike Kleinbb1933e2020-12-02 15:45:29 -0600883 if (sampling.useCubic) {
Mike Reedf8a6b5b2020-07-10 08:36:42 -0400884 // All bicubic samples have the same fractional offset (fx,fy) from the center.
885 // They're either the 16 corners of a 3x3 grid/ surrounding (x,y) at (0.5,0.5) off-center.
886 skvm::F32 fx = fract(local.x + 0.5f),
887 fy = fract(local.y + 0.5f);
Mike Reed3d30ca62020-07-22 16:55:02 -0400888 skvm::F32 wx[4],
889 wy[4];
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600890
Mike Kleinbb1933e2020-12-02 15:45:29 -0600891 SkM44 weights = CubicResamplerMatrix(sampling.cubic.B, sampling.cubic.C);
Mike Reed3d30ca62020-07-22 16:55:02 -0400892
Mike Kleind3184892020-07-22 17:39:20 -0500893 auto dot = [](const skvm::F32 a[], const skvm::F32 b[]) {
894 return a[0]*b[0] + a[1]*b[1] + a[2]*b[2] + a[3]*b[3];
895 };
896 const skvm::F32 tmpx[] = { p->splat(1.0f), fx, fx*fx, fx*fx*fx };
897 const skvm::F32 tmpy[] = { p->splat(1.0f), fy, fy*fy, fy*fy*fy };
Mike Reed3d30ca62020-07-22 16:55:02 -0400898
Mike Kleind3184892020-07-22 17:39:20 -0500899 for (int row = 0; row < 4; ++row) {
900 SkV4 r = weights.row(row);
901 skvm::F32 ru[] = {
902 p->uniformF(uniforms->pushF(r[0])),
903 p->uniformF(uniforms->pushF(r[1])),
904 p->uniformF(uniforms->pushF(r[2])),
905 p->uniformF(uniforms->pushF(r[3])),
Mike Reed3d30ca62020-07-22 16:55:02 -0400906 };
Mike Kleind3184892020-07-22 17:39:20 -0500907 wx[row] = dot(ru, tmpx);
908 wy[row] = dot(ru, tmpy);
Mike Reed3d30ca62020-07-22 16:55:02 -0400909 }
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600910
Mike Reedf8a6b5b2020-07-10 08:36:42 -0400911 skvm::Color c;
912 c.r = c.g = c.b = c.a = p->splat(0.0f);
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600913
Mike Reedf8a6b5b2020-07-10 08:36:42 -0400914 skvm::F32 sy = local.y - 1.5f;
915 for (int j = 0; j < 4; j++, sy += 1.0f) {
916 skvm::F32 sx = local.x - 1.5f;
917 for (int i = 0; i < 4; i++, sx += 1.0f) {
918 skvm::Color s = sample_texel(u, sx,sy);
919 skvm::F32 w = wx[i] * wy[j];
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600920
Mike Reedf8a6b5b2020-07-10 08:36:42 -0400921 c.r += s.r * w;
922 c.g += s.g * w;
923 c.b += s.b * w;
924 c.a += s.a * w;
925 }
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600926 }
Mike Reedf8a6b5b2020-07-10 08:36:42 -0400927 return c;
Mike Kleinbb1933e2020-12-02 15:45:29 -0600928 } else if (sampling.filter == SkFilterMode::kLinear) {
Mike Reed3d58d5a2020-11-23 13:32:36 -0500929 // Our four sample points are the corners of a logical 1x1 pixel
930 // box surrounding (x,y) at (0.5,0.5) off-center.
931 skvm::F32 left = local.x - 0.5f,
932 top = local.y - 0.5f,
933 right = local.x + 0.5f,
934 bottom = local.y + 0.5f;
935
936 // The fractional parts of right and bottom are our lerp factors in x and y respectively.
937 skvm::F32 fx = fract(right ),
938 fy = fract(bottom);
939
940 return lerp(lerp(sample_texel(u, left,top ), sample_texel(u, right,top ), fx),
941 lerp(sample_texel(u, left,bottom), sample_texel(u, right,bottom), fx), fy);
942 } else {
Mike Kleinbb1933e2020-12-02 15:45:29 -0600943 SkASSERT(sampling.filter == SkFilterMode::kNearest);
Mike Reed3d58d5a2020-11-23 13:32:36 -0500944 return sample_texel(u, local.x,local.y);
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600945 }
Mike Reedf8a6b5b2020-07-10 08:36:42 -0400946 };
947
Mike Reed8c1ad7e2020-12-02 20:41:52 -0500948 skvm::Color c = sample_level(upper, upperInv, upperLocal);
Mike Reedf8a6b5b2020-07-10 08:36:42 -0400949 if (lower) {
Mike Reedf8a6b5b2020-07-10 08:36:42 -0400950 auto lowerLocal = SkShaderBase::ApplyMatrix(p, lowerInv, origLocal, uniforms);
951 // lower * weight + upper * (1 - weight)
952 c = lerp(c,
953 sample_level(*lower, lowerInv, lowerLocal),
954 p->uniformF(uniforms->pushF(lowerWeight)));
Mike Klein921236b2020-02-13 11:20:54 -0600955 }
Mike Kleind67a5c32020-01-06 16:01:01 -0600956
Mike Klein921236b2020-02-13 11:20:54 -0600957 // If the input is opaque and we're not in decal mode, that means the output is too.
958 // Forcing *a to 1.0 here will retroactively skip any work we did to interpolate sample alphas.
959 if (input_is_opaque
960 && fTileModeX != SkTileMode::kDecal
961 && fTileModeY != SkTileMode::kDecal) {
Mike Reed6352f002020-03-14 23:30:10 -0400962 c.a = p->splat(1.0f);
Mike Klein921236b2020-02-13 11:20:54 -0600963 }
964
Mike Klein913a6f52020-03-23 11:06:25 -0500965 // Alpha-only images get their color from the paint (already converted to dst color space).
Mike Reed8c1ad7e2020-12-02 20:41:52 -0500966 SkColorSpace* cs = upper.colorSpace();
967 SkAlphaType at = upper.alphaType();
968 if (SkColorTypeIsAlphaOnly(upper.colorType())) {
Mike Klein913a6f52020-03-23 11:06:25 -0500969 c.r = paint.r;
970 c.g = paint.g;
971 c.b = paint.b;
972
973 cs = dst.colorSpace();
974 at = kUnpremul_SkAlphaType;
975 }
976
Mike Kleinbb1933e2020-12-02 15:45:29 -0600977 if (sampling.useCubic) {
Mike Klein5c660e02020-01-08 11:36:35 -0600978 // Bicubic filtering naturally produces out of range values on both sides of [0,1].
Mike Reedf3b9a302020-04-01 13:18:02 -0400979 c.a = clamp01(c.a);
Mike Klein3f83bfd2020-01-07 12:01:50 -0600980
Mike Klein913a6f52020-03-23 11:06:25 -0500981 skvm::F32 limit = (at == kUnpremul_SkAlphaType || fClampAsIfUnpremul)
Mike Klein3f83bfd2020-01-07 12:01:50 -0600982 ? p->splat(1.0f)
Mike Reed6352f002020-03-14 23:30:10 -0400983 : c.a;
Mike Reedf3b9a302020-04-01 13:18:02 -0400984 c.r = clamp(c.r, 0.0f, limit);
985 c.g = clamp(c.g, 0.0f, limit);
986 c.b = clamp(c.b, 0.0f, limit);
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600987 }
Mike Klein4bc86d52020-01-06 18:39:42 -0600988
Mike Kleinef0fa432020-07-29 12:35:45 -0500989 return SkColorSpaceXformSteps{cs,at, dst.colorSpace(),dst.alphaType()}.program(p, uniforms, c);
Mike Kleinf6a715b2019-12-30 15:24:18 -0600990}