blob: f9d6dab598d6fceccc12feb109019afa08176590 [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
Ben Wagner729a23f2019-05-17 16:29:34 -04008#include "src/core/SkArenaAlloc.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -05009#include "src/core/SkBitmapController.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "src/core/SkColorSpacePriv.h"
11#include "src/core/SkColorSpaceXformSteps.h"
Brian Osman449b1152020-04-15 16:43:00 -040012#include "src/core/SkMatrixProvider.h"
Mike Klein37bc8f92019-10-21 13:10:07 -050013#include "src/core/SkOpts.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050014#include "src/core/SkRasterPipeline.h"
15#include "src/core/SkReadBuffer.h"
Mike Klein8e717442020-01-07 10:22:33 -060016#include "src/core/SkVM.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050017#include "src/core/SkWriteBuffer.h"
18#include "src/image/SkImage_Base.h"
19#include "src/shaders/SkBitmapProcShader.h"
20#include "src/shaders/SkEmptyShader.h"
21#include "src/shaders/SkImageShader.h"
reed856e9d92015-09-30 12:21:45 -070022
Mike Reed587d0822017-06-23 16:49:12 -040023/**
24 * We are faster in clamp, so always use that tiling when we can.
25 */
Mike Reedfae8fce2019-04-03 10:27:45 -040026static SkTileMode optimize(SkTileMode tm, int dimension) {
Mike Reed587d0822017-06-23 16:49:12 -040027 SkASSERT(dimension > 0);
Mike Reed2e3c9552017-06-23 21:33:58 -040028#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
29 // need to update frameworks/base/libs/hwui/tests/unit/SkiaBehaviorTests.cpp:55 to allow
30 // for transforming to clamp.
Mike Reedfae8fce2019-04-03 10:27:45 -040031 return tm;
Mike Reed2e3c9552017-06-23 21:33:58 -040032#else
Mike Reedfae8fce2019-04-03 10:27:45 -040033 return dimension == 1 ? SkTileMode::kClamp : tm;
Mike Reed2e3c9552017-06-23 21:33:58 -040034#endif
Mike Reed587d0822017-06-23 16:49:12 -040035}
36
Mike Klein1f313092018-01-03 10:30:21 -050037SkImageShader::SkImageShader(sk_sp<SkImage> img,
Mike Reede25b4472019-04-02 17:49:12 -040038 SkTileMode tmx, SkTileMode tmy,
Mike Klein1f313092018-01-03 10:30:21 -050039 const SkMatrix* localMatrix,
40 bool clampAsIfUnpremul)
41 : INHERITED(localMatrix)
reed6b2d7ac2016-08-11 06:42:26 -070042 , fImage(std::move(img))
Mike Reed587d0822017-06-23 16:49:12 -040043 , fTileModeX(optimize(tmx, fImage->width()))
44 , fTileModeY(optimize(tmy, fImage->height()))
Mike Klein1f313092018-01-03 10:30:21 -050045 , fClampAsIfUnpremul(clampAsIfUnpremul)
reed856e9d92015-09-30 12:21:45 -070046{}
47
Mike Klein1f313092018-01-03 10:30:21 -050048// fClampAsIfUnpremul is always false when constructed through public APIs,
49// so there's no need to read or write it here.
50
reed60c9b582016-04-03 09:11:13 -070051sk_sp<SkFlattenable> SkImageShader::CreateProc(SkReadBuffer& buffer) {
Mike Reede25b4472019-04-02 17:49:12 -040052 auto tmx = buffer.read32LE<SkTileMode>(SkTileMode::kLastTileMode);
53 auto tmy = buffer.read32LE<SkTileMode>(SkTileMode::kLastTileMode);
Mike Klein1f313092018-01-03 10:30:21 -050054 SkMatrix localMatrix;
55 buffer.readMatrix(&localMatrix);
reeda9ca05c2016-08-11 03:55:15 -070056 sk_sp<SkImage> img = buffer.readImage();
reed856e9d92015-09-30 12:21:45 -070057 if (!img) {
58 return nullptr;
59 }
Mike Reede25b4472019-04-02 17:49:12 -040060 return SkImageShader::Make(std::move(img), tmx, tmy, &localMatrix);
reed856e9d92015-09-30 12:21:45 -070061}
62
63void SkImageShader::flatten(SkWriteBuffer& buffer) const {
Mike Reedfae8fce2019-04-03 10:27:45 -040064 buffer.writeUInt((unsigned)fTileModeX);
65 buffer.writeUInt((unsigned)fTileModeY);
reed856e9d92015-09-30 12:21:45 -070066 buffer.writeMatrix(this->getLocalMatrix());
reed6b2d7ac2016-08-11 06:42:26 -070067 buffer.writeImage(fImage.get());
Mike Klein1f313092018-01-03 10:30:21 -050068 SkASSERT(fClampAsIfUnpremul == false);
reed856e9d92015-09-30 12:21:45 -070069}
70
71bool SkImageShader::isOpaque() const {
Mike Reedfae8fce2019-04-03 10:27:45 -040072 return fImage->isOpaque() &&
73 fTileModeX != SkTileMode::kDecal && fTileModeY != SkTileMode::kDecal;
reed856e9d92015-09-30 12:21:45 -070074}
75
Mike Reede92aae62018-10-17 10:21:51 -040076#ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
Florin Malitaaf2769d2018-04-04 13:46:35 -040077static bool legacy_shader_can_handle(const SkMatrix& inv) {
Mike Klein37bc8f92019-10-21 13:10:07 -050078 if (inv.hasPerspective()) {
79 return false;
80 }
81
82 // Scale+translate methods are always present, but affine might not be.
83 if (!SkOpts::S32_alpha_D32_filter_DXDY && !inv.isScaleTranslate()) {
Mike Reeda12c4192018-02-01 16:34:03 -050084 return false;
85 }
86
87 // legacy code uses SkFixed 32.32, so ensure the inverse doesn't map device coordinates
88 // out of range.
89 const SkScalar max_dev_coord = 32767.0f;
Mike Klein37bc8f92019-10-21 13:10:07 -050090 const SkRect src = inv.mapRect(SkRect::MakeWH(max_dev_coord, max_dev_coord));
Mike Reeda12c4192018-02-01 16:34:03 -050091
Mike Reed1eb5ca42018-03-08 14:20:52 -050092 // take 1/4 of max signed 32bits so we have room to subtract local values
Kevin Lubickf76da632020-01-28 10:39:56 -050093 const SkScalar max_fixed32dot32 = float(SK_MaxS32) * 0.25f;
Mike Reeda12c4192018-02-01 16:34:03 -050094 if (!SkRect::MakeLTRB(-max_fixed32dot32, -max_fixed32dot32,
Mike Klein37bc8f92019-10-21 13:10:07 -050095 +max_fixed32dot32, +max_fixed32dot32).contains(src)) {
Mike Reeda12c4192018-02-01 16:34:03 -050096 return false;
97 }
98
99 // legacy shader impl should be able to handle these matrices
100 return true;
101}
102
Florin Malita4aed1382017-05-25 10:38:07 -0400103SkShaderBase::Context* SkImageShader::onMakeContext(const ContextRec& rec,
104 SkArenaAlloc* alloc) const {
Brian Osman0e189372018-10-19 11:58:29 -0400105 if (fImage->alphaType() == kUnpremul_SkAlphaType) {
Florin Malitaaf2769d2018-04-04 13:46:35 -0400106 return nullptr;
107 }
Brian Osmanb70fd912018-10-22 16:10:44 -0400108 if (fImage->colorType() != kN32_SkColorType) {
109 return nullptr;
110 }
Florin Malitaaf2769d2018-04-04 13:46:35 -0400111 if (fTileModeX != fTileModeY) {
112 return nullptr;
113 }
Mike Reedfae8fce2019-04-03 10:27:45 -0400114 if (fTileModeX == SkTileMode::kDecal || fTileModeY == SkTileMode::kDecal) {
Florin Malitaaf2769d2018-04-04 13:46:35 -0400115 return nullptr;
116 }
117
Mike Kleindac694d2018-12-18 10:13:52 -0500118 // SkBitmapProcShader stores bitmap coordinates in a 16bit buffer,
Mike Klein67761eb2018-12-18 10:16:53 -0500119 // so it can't handle bitmaps larger than 65535.
Mike Kleindac694d2018-12-18 10:13:52 -0500120 //
Mike Klein67761eb2018-12-18 10:16:53 -0500121 // We back off another bit to 32767 to make small amounts of
122 // intermediate math safe, e.g. in
123 //
124 // SkFixed fx = ...;
125 // fx = tile(fx + SK_Fixed1);
126 //
127 // we want to make sure (fx + SK_Fixed1) never overflows.
128 if (fImage-> width() > 32767 ||
129 fImage->height() > 32767) {
Mike Kleindac694d2018-12-18 10:13:52 -0500130 return nullptr;
131 }
132
Florin Malitaaf2769d2018-04-04 13:46:35 -0400133 SkMatrix inv;
134 if (!this->computeTotalInverse(*rec.fMatrix, rec.fLocalMatrix, &inv) ||
135 !legacy_shader_can_handle(inv)) {
136 return nullptr;
137 }
138
Mike Reed011d1662019-02-28 17:19:25 -0500139 if (!rec.isLegacyCompatible(fImage->colorSpace())) {
140 return nullptr;
141 }
142
reed320a40d2016-08-02 06:12:06 -0700143 return SkBitmapProcLegacyShader::MakeContext(*this, fTileModeX, fTileModeY,
Mike Reed64acf4f2019-08-01 15:35:20 -0400144 as_IB(fImage.get()), rec, alloc);
reed856e9d92015-09-30 12:21:45 -0700145}
Mike Reede92aae62018-10-17 10:21:51 -0400146#endif
reed856e9d92015-09-30 12:21:45 -0700147
Mike Reedfae8fce2019-04-03 10:27:45 -0400148SkImage* SkImageShader::onIsAImage(SkMatrix* texM, SkTileMode xy[]) const {
reedf1ac1822016-08-01 11:24:14 -0700149 if (texM) {
150 *texM = this->getLocalMatrix();
151 }
152 if (xy) {
Mike Reedfae8fce2019-04-03 10:27:45 -0400153 xy[0] = fTileModeX;
154 xy[1] = fTileModeY;
reedf1ac1822016-08-01 11:24:14 -0700155 }
156 return const_cast<SkImage*>(fImage.get());
157}
158
Mike Klein1f313092018-01-03 10:30:21 -0500159sk_sp<SkShader> SkImageShader::Make(sk_sp<SkImage> image,
Mike Reede25b4472019-04-02 17:49:12 -0400160 SkTileMode tmx, SkTileMode tmy,
Mike Klein1f313092018-01-03 10:30:21 -0500161 const SkMatrix* localMatrix,
162 bool clampAsIfUnpremul) {
Mike Kleindac694d2018-12-18 10:13:52 -0500163 if (!image) {
Herb Derbybfdc87a2017-02-14 15:06:23 +0000164 return sk_make_sp<SkEmptyShader>();
reed320a40d2016-08-02 06:12:06 -0700165 }
Mike Reede25b4472019-04-02 17:49:12 -0400166 return sk_sp<SkShader>{ new SkImageShader(image, tmx, tmy, localMatrix, clampAsIfUnpremul) };
reed856e9d92015-09-30 12:21:45 -0700167}
168
reed856e9d92015-09-30 12:21:45 -0700169///////////////////////////////////////////////////////////////////////////////////////////////////
170
171#if SK_SUPPORT_GPU
172
Mike Kleinc0bd9f92019-04-23 12:05:21 -0500173#include "include/private/GrRecordingContext.h"
174#include "src/gpu/GrCaps.h"
Brian Salomon4bc0c1f2019-09-30 15:12:27 -0400175#include "src/gpu/GrColorInfo.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -0500176#include "src/gpu/GrRecordingContextPriv.h"
177#include "src/gpu/SkGr.h"
178#include "src/gpu/effects/GrBicubicEffect.h"
Brian Salomonb8f098d2020-01-07 11:15:44 -0500179#include "src/gpu/effects/GrTextureEffect.h"
reed856e9d92015-09-30 12:21:45 -0700180
Brian Salomonaff329b2017-08-11 09:40:37 -0400181std::unique_ptr<GrFragmentProcessor> SkImageShader::asFragmentProcessor(
Mike Reede3429e62018-01-19 11:43:34 -0500182 const GrFPArgs& args) const {
Florin Malita52f02912020-03-09 16:33:17 -0400183 const auto lm = this->totalLocalMatrix(args.fPreLocalMatrix);
reed856e9d92015-09-30 12:21:45 -0700184 SkMatrix lmInverse;
Florin Malitac6c5ead2018-04-11 15:33:40 -0400185 if (!lm->invert(&lmInverse)) {
reed856e9d92015-09-30 12:21:45 -0700186 return nullptr;
187 }
reed856e9d92015-09-30 12:21:45 -0700188
Brian Salomon694ec492020-04-14 13:39:31 -0400189 GrSamplerState::WrapMode wmX = SkTileModeToWrapMode(fTileModeX),
190 wmY = SkTileModeToWrapMode(fTileModeY);
Michael Ludwigbe315a22018-12-17 09:50:51 -0500191
reed856e9d92015-09-30 12:21:45 -0700192 // Must set wrap and filter on the sampler before requesting a texture. In two places below
193 // we check the matrix scale factors to determine how to interpret the filter quality setting.
194 // This completely ignores the complexity of the drawVertices case where explicit local coords
195 // are provided by the caller.
196 bool doBicubic;
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400197 GrSamplerState::Filter textureFilterMode = GrSkFilterQualityToGrFilterMode(
Brian Osman449b1152020-04-15 16:43:00 -0400198 fImage->width(), fImage->height(), args.fFilterQuality,
199 args.fMatrixProvider.localToDevice(), *lm,
Robert Phillips9da87e02019-02-04 13:26:26 -0500200 args.fContext->priv().options().fSharpenMipmappedTextures, &doBicubic);
Brian Salomonecbb0fb2020-02-28 18:07:32 -0500201 GrMipMapped mipMapped = GrMipMapped::kNo;
202 if (textureFilterMode == GrSamplerState::Filter::kMipMap) {
203 mipMapped = GrMipMapped::kYes;
204 }
205 GrSurfaceProxyView view = as_IB(fImage)->refView(args.fContext, mipMapped);
Greg Danielfebdedf2020-02-05 17:06:27 -0500206 if (!view) {
reed856e9d92015-09-30 12:21:45 -0700207 return nullptr;
208 }
209
Brian Salomonfc118442019-11-22 19:09:27 -0500210 SkAlphaType srcAlphaType = fImage->alphaType();
Robert Phillipsb726d582017-03-09 16:36:32 -0500211
Brian Salomonca6b2f42020-01-24 11:31:21 -0500212 const auto& caps = *args.fContext->priv().caps();
213
Brian Salomonaff329b2017-08-11 09:40:37 -0400214 std::unique_ptr<GrFragmentProcessor> inner;
reed856e9d92015-09-30 12:21:45 -0700215 if (doBicubic) {
Brian Salomona86fc7a2019-05-28 20:42:58 -0400216 static constexpr auto kDir = GrBicubicEffect::Direction::kXY;
Brian Salomonecbb0fb2020-02-28 18:07:32 -0500217 inner = GrBicubicEffect::Make(std::move(view), srcAlphaType, lmInverse, wmX, wmY, kDir,
Brian Salomond0d033a2020-02-18 16:59:28 -0500218 caps);
reed856e9d92015-09-30 12:21:45 -0700219 } else {
Brian Salomonecbb0fb2020-02-28 18:07:32 -0500220 GrSamplerState samplerState(wmX, wmY, textureFilterMode);
Greg Danield2ccbb52020-02-05 10:45:39 -0500221 inner = GrTextureEffect::Make(std::move(view), srcAlphaType, lmInverse, samplerState, caps);
reed856e9d92015-09-30 12:21:45 -0700222 }
Brian Salomonfc118442019-11-22 19:09:27 -0500223 inner = GrColorSpaceXformEffect::Make(std::move(inner), fImage->colorSpace(), srcAlphaType,
224 args.fDstColorInfo->colorSpace());
Greg Danielc594e622019-10-15 14:01:49 -0400225
226 bool isAlphaOnly = SkColorTypeIsAlphaOnly(fImage->colorType());
Robert Phillipsb726d582017-03-09 16:36:32 -0500227 if (isAlphaOnly) {
bungeman06ca8ec2016-06-09 08:01:03 -0700228 return inner;
Brian Salomonc0d79e52019-04-10 15:02:11 -0400229 } else if (args.fInputColorIsOpaque) {
230 return GrFragmentProcessor::OverrideInput(std::move(inner), SK_PMColor4fWHITE, false);
reed856e9d92015-09-30 12:21:45 -0700231 }
Mike Reed28eaed22018-02-01 11:24:53 -0500232 return GrFragmentProcessor::MulChildByInputAlpha(std::move(inner));
reed856e9d92015-09-30 12:21:45 -0700233}
234
235#endif
reed320a40d2016-08-02 06:12:06 -0700236
237///////////////////////////////////////////////////////////////////////////////////////////////////
Mike Kleinc0bd9f92019-04-23 12:05:21 -0500238#include "src/core/SkImagePriv.h"
reed320a40d2016-08-02 06:12:06 -0700239
Mike Reede25b4472019-04-02 17:49:12 -0400240sk_sp<SkShader> SkMakeBitmapShader(const SkBitmap& src, SkTileMode tmx, SkTileMode tmy,
241 const SkMatrix* localMatrix, SkCopyPixelsMode cpm) {
Herb Derbybfdc87a2017-02-14 15:06:23 +0000242 return SkImageShader::Make(SkMakeImageFromRasterBitmap(src, cpm),
243 tmx, tmy, localMatrix);
reed320a40d2016-08-02 06:12:06 -0700244}
245
Michael Ludwigc47e81b2019-04-02 15:18:02 -0400246sk_sp<SkShader> SkMakeBitmapShaderForPaint(const SkPaint& paint, const SkBitmap& src,
Mike Reede25b4472019-04-02 17:49:12 -0400247 SkTileMode tmx, SkTileMode tmy,
Michael Ludwigc47e81b2019-04-02 15:18:02 -0400248 const SkMatrix* localMatrix, SkCopyPixelsMode mode) {
249 auto s = SkMakeBitmapShader(src, tmx, tmy, localMatrix, mode);
250 if (!s) {
251 return nullptr;
252 }
253 if (src.colorType() == kAlpha_8_SkColorType && paint.getShader()) {
254 // Compose the image shader with the paint's shader. Alpha images+shaders should output the
255 // texture's alpha multiplied by the shader's color. DstIn (d*sa) will achieve this with
256 // the source image and dst shader (MakeBlend takes dst first, src second).
Mike Reedc8bea7d2019-04-09 13:55:36 -0400257 s = SkShaders::Blend(SkBlendMode::kDstIn, paint.refShader(), std::move(s));
Michael Ludwigc47e81b2019-04-02 15:18:02 -0400258 }
259 return s;
260}
261
Brian Salomon23356442018-11-30 15:33:19 -0500262void SkShaderBase::RegisterFlattenables() { SK_REGISTER_FLATTENABLE(SkImageShader); }
reed320a40d2016-08-02 06:12:06 -0700263
Mike Reed9318a6c2019-08-16 16:16:25 -0400264class SkImageStageUpdater : public SkStageUpdater {
265public:
Mike Reed8845c372019-12-19 13:22:08 -0500266 SkImageStageUpdater(const SkImageShader* shader, bool usePersp)
Mike Reed0ae1b3d2020-03-04 17:47:43 -0500267 : fShader(shader)
268 , fUsePersp(usePersp || as_SB(shader)->getLocalMatrix().hasPerspective())
Mike Reed8845c372019-12-19 13:22:08 -0500269 {}
Mike Reed9318a6c2019-08-16 16:16:25 -0400270
Mike Reed8845c372019-12-19 13:22:08 -0500271 const SkImageShader* fShader;
272 const bool fUsePersp; // else use affine
273
274 // large enough for perspective, though often we just use 2x3
275 float fMatrixStorage[9];
Mike Reed9318a6c2019-08-16 16:16:25 -0400276
277#if 0 // TODO: when we support mipmaps
278 SkRasterPipeline_GatherCtx* fGather;
279 SkRasterPipeline_TileCtx* fLimitX;
280 SkRasterPipeline_TileCtx* fLimitY;
281 SkRasterPipeline_DecalTileCtx* fDecal;
282#endif
283
Mike Reed8845c372019-12-19 13:22:08 -0500284 void append_matrix_stage(SkRasterPipeline* p) {
285 if (fUsePersp) {
286 p->append(SkRasterPipeline::matrix_perspective, fMatrixStorage);
287 } else {
288 p->append(SkRasterPipeline::matrix_2x3, fMatrixStorage);
289 }
290 }
291
Mike Reed9318a6c2019-08-16 16:16:25 -0400292 bool update(const SkMatrix& ctm, const SkMatrix* localM) override {
293 SkMatrix matrix;
Mike Reed8845c372019-12-19 13:22:08 -0500294 if (fShader->computeTotalInverse(ctm, localM, &matrix)) {
295 if (fUsePersp) {
296 matrix.get9(fMatrixStorage);
297 } else {
Mike Reed0ae1b3d2020-03-04 17:47:43 -0500298 // if we get here, matrix should be affine. If it isn't, then defensively we
299 // won't draw (by returning false), but we should work to never let this
300 // happen (i.e. better preflight by the caller to know ahead of time that we
301 // may encounter perspective, either in the CTM, or in the localM).
302 //
303 // See https://bugs.chromium.org/p/skia/issues/detail?id=10004
304 //
305 if (!matrix.asAffine(fMatrixStorage)) {
306 SkASSERT(false);
307 return false;
308 }
Mike Reed8845c372019-12-19 13:22:08 -0500309 }
310 return true;
311 }
312 return false;
Mike Reed9318a6c2019-08-16 16:16:25 -0400313 }
314};
315
Mike Klein6dbd7ff2020-01-06 11:50:37 -0600316static void tweak_quality_and_inv_matrix(SkFilterQuality* quality, SkMatrix* matrix) {
317 // When the matrix is just an integer translate, bilerp == nearest neighbor.
318 if (*quality == kLow_SkFilterQuality &&
319 matrix->getType() <= SkMatrix::kTranslate_Mask &&
320 matrix->getTranslateX() == (int)matrix->getTranslateX() &&
321 matrix->getTranslateY() == (int)matrix->getTranslateY()) {
322 *quality = kNone_SkFilterQuality;
323 }
324
325 // See skia:4649 and the GM image_scale_aligned.
326 if (*quality == kNone_SkFilterQuality) {
327 if (matrix->getScaleX() >= 0) {
328 matrix->setTranslateX(nextafterf(matrix->getTranslateX(),
329 floorf(matrix->getTranslateX())));
330 }
331 if (matrix->getScaleY() >= 0) {
332 matrix->setTranslateY(nextafterf(matrix->getTranslateY(),
333 floorf(matrix->getTranslateY())));
334 }
335 }
336}
337
Mike Reed9318a6c2019-08-16 16:16:25 -0400338bool SkImageShader::doStages(const SkStageRec& rec, SkImageStageUpdater* updater) const {
Mike Reed8845c372019-12-19 13:22:08 -0500339 if (updater && rec.fPaint.getFilterQuality() == kMedium_SkFilterQuality) {
340 // TODO: medium: recall RequestBitmap and update width/height accordingly
Mike Reed9318a6c2019-08-16 16:16:25 -0400341 return false;
342 }
343
Mike Reed1d8c42e2017-08-29 14:58:19 -0400344 SkRasterPipeline* p = rec.fPipeline;
345 SkArenaAlloc* alloc = rec.fAlloc;
Mike Reed9318a6c2019-08-16 16:16:25 -0400346 auto quality = rec.fPaint.getFilterQuality();
Mike Reed1d8c42e2017-08-29 14:58:19 -0400347
Florin Malita7558e4d2018-02-07 10:05:53 -0500348 SkMatrix matrix;
349 if (!this->computeTotalInverse(rec.fCTM, rec.fLocalM, &matrix)) {
Mike Klein06a65e22016-11-17 12:39:09 -0500350 return false;
351 }
Mike Klein06a65e22016-11-17 12:39:09 -0500352
Mike Reed64acf4f2019-08-01 15:35:20 -0400353 const auto* state = SkBitmapController::RequestBitmap(as_IB(fImage.get()),
354 matrix, quality, alloc);
Mike Kleinf447dee2016-11-29 16:37:12 -0500355 if (!state) {
356 return false;
357 }
358
359 const SkPixmap& pm = state->pixmap();
360 matrix = state->invMatrix();
361 quality = state->quality();
362 auto info = pm.info();
363
Mike Kleine8de0242018-03-10 12:37:11 -0500364 p->append(SkRasterPipeline::seed_shader);
Mike Reed9318a6c2019-08-16 16:16:25 -0400365
366 if (updater) {
Mike Reed8845c372019-12-19 13:22:08 -0500367 updater->append_matrix_stage(p);
Mike Reed9318a6c2019-08-16 16:16:25 -0400368 } else {
Mike Klein6dbd7ff2020-01-06 11:50:37 -0600369 tweak_quality_and_inv_matrix(&quality, &matrix);
Mike Reed9318a6c2019-08-16 16:16:25 -0400370 p->append_matrix(alloc, matrix);
371 }
Mike Klein06a65e22016-11-17 12:39:09 -0500372
Mike Kleinb11ab572018-10-24 06:42:14 -0400373 auto gather = alloc->make<SkRasterPipeline_GatherCtx>();
Mike Klein1fa9c432017-12-11 09:59:47 -0500374 gather->pixels = pm.addr();
Mike Klein968af432017-07-18 16:31:55 -0400375 gather->stride = pm.rowBytesAsPixels();
Mike Kleinf3b4e162017-09-22 15:32:59 -0400376 gather->width = pm.width();
377 gather->height = pm.height();
Mike Klein0a904492017-04-12 12:52:48 -0400378
Mike Kleinb11ab572018-10-24 06:42:14 -0400379 auto limit_x = alloc->make<SkRasterPipeline_TileCtx>(),
380 limit_y = alloc->make<SkRasterPipeline_TileCtx>();
Mike Reed51e46d52017-06-23 14:21:25 -0400381 limit_x->scale = pm.width();
382 limit_x->invScale = 1.0f / pm.width();
383 limit_y->scale = pm.height();
384 limit_y->invScale = 1.0f / pm.height();
Mike Kleinfc84dc52017-05-11 15:29:31 -0400385
Mike Kleinb11ab572018-10-24 06:42:14 -0400386 SkRasterPipeline_DecalTileCtx* decal_ctx = nullptr;
Mike Reedfae8fce2019-04-03 10:27:45 -0400387 bool decal_x_and_y = fTileModeX == SkTileMode::kDecal && fTileModeY == SkTileMode::kDecal;
388 if (fTileModeX == SkTileMode::kDecal || fTileModeY == SkTileMode::kDecal) {
Mike Kleinb11ab572018-10-24 06:42:14 -0400389 decal_ctx = alloc->make<SkRasterPipeline_DecalTileCtx>();
Mike Reeddfc0e912018-02-16 12:40:18 -0500390 decal_ctx->limit_x = limit_x->scale;
391 decal_ctx->limit_y = limit_y->scale;
392 }
393
Mike Reed9318a6c2019-08-16 16:16:25 -0400394#if 0 // TODO: when we support kMedium
395 if (updator && (quality == kMedium_SkFilterQuality)) {
396 // if we change levels in mipmap, we need to update the scales (and invScales)
397 updator->fGather = gather;
398 updator->fLimitX = limit_x;
399 updator->fLimitY = limit_y;
400 updator->fDecal = decal_ctx;
401 }
402#endif
403
Mike Kleinb04c3522016-11-28 11:55:58 -0500404 auto append_tiling_and_gather = [&] {
Mike Reeddfc0e912018-02-16 12:40:18 -0500405 if (decal_x_and_y) {
406 p->append(SkRasterPipeline::decal_x_and_y, decal_ctx);
407 } else {
408 switch (fTileModeX) {
Mike Reedfae8fce2019-04-03 10:27:45 -0400409 case SkTileMode::kClamp: /* The gather_xxx stage will clamp for us. */ break;
410 case SkTileMode::kMirror: p->append(SkRasterPipeline::mirror_x, limit_x); break;
411 case SkTileMode::kRepeat: p->append(SkRasterPipeline::repeat_x, limit_x); break;
412 case SkTileMode::kDecal: p->append(SkRasterPipeline::decal_x, decal_ctx); break;
Mike Reeddfc0e912018-02-16 12:40:18 -0500413 }
414 switch (fTileModeY) {
Mike Reedfae8fce2019-04-03 10:27:45 -0400415 case SkTileMode::kClamp: /* The gather_xxx stage will clamp for us. */ break;
416 case SkTileMode::kMirror: p->append(SkRasterPipeline::mirror_y, limit_y); break;
417 case SkTileMode::kRepeat: p->append(SkRasterPipeline::repeat_y, limit_y); break;
418 case SkTileMode::kDecal: p->append(SkRasterPipeline::decal_y, decal_ctx); break;
Mike Reeddfc0e912018-02-16 12:40:18 -0500419 }
Mike Kleinf7f883b2016-11-21 15:09:45 -0500420 }
Mike Reeddfc0e912018-02-16 12:40:18 -0500421
Mike Kleinac568a92018-01-25 09:09:32 -0500422 void* ctx = gather;
Mike Kleinf7f883b2016-11-21 15:09:45 -0500423 switch (info.colorType()) {
Mike Kleinac568a92018-01-25 09:09:32 -0500424 case kAlpha_8_SkColorType: p->append(SkRasterPipeline::gather_a8, ctx); break;
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400425 case kA16_unorm_SkColorType: p->append(SkRasterPipeline::gather_a16, ctx); break;
426 case kA16_float_SkColorType: p->append(SkRasterPipeline::gather_af16, ctx); break;
Mike Kleinac568a92018-01-25 09:09:32 -0500427 case kRGB_565_SkColorType: p->append(SkRasterPipeline::gather_565, ctx); break;
428 case kARGB_4444_SkColorType: p->append(SkRasterPipeline::gather_4444, ctx); break;
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400429 case kR8G8_unorm_SkColorType: p->append(SkRasterPipeline::gather_rg88, ctx); break;
430 case kR16G16_unorm_SkColorType: p->append(SkRasterPipeline::gather_rg1616, ctx); break;
431 case kR16G16_float_SkColorType: p->append(SkRasterPipeline::gather_rgf16, ctx); break;
Mike Kleinac568a92018-01-25 09:09:32 -0500432 case kRGBA_8888_SkColorType: p->append(SkRasterPipeline::gather_8888, ctx); break;
433 case kRGBA_1010102_SkColorType: p->append(SkRasterPipeline::gather_1010102, ctx); break;
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400434 case kR16G16B16A16_unorm_SkColorType:
435 p->append(SkRasterPipeline::gather_16161616,ctx); break;
Mike Kleinb70990e2019-02-28 10:03:27 -0600436 case kRGBA_F16Norm_SkColorType:
Mike Kleinac568a92018-01-25 09:09:32 -0500437 case kRGBA_F16_SkColorType: p->append(SkRasterPipeline::gather_f16, ctx); break;
Mike Klein37854712018-06-26 11:43:06 -0400438 case kRGBA_F32_SkColorType: p->append(SkRasterPipeline::gather_f32, ctx); break;
Mike Kleinac568a92018-01-25 09:09:32 -0500439
Mike Kleinb1df5e52018-10-17 17:06:03 -0400440 case kGray_8_SkColorType: p->append(SkRasterPipeline::gather_a8, ctx);
441 p->append(SkRasterPipeline::alpha_to_gray ); break;
Mike Klein1a3eb522018-10-18 10:11:00 -0400442
Mike Kleinac568a92018-01-25 09:09:32 -0500443 case kRGB_888x_SkColorType: p->append(SkRasterPipeline::gather_8888, ctx);
444 p->append(SkRasterPipeline::force_opaque ); break;
Mike Klein1a3eb522018-10-18 10:11:00 -0400445
Mike Kleinf7eb0542020-02-11 12:19:08 -0600446 case kBGRA_1010102_SkColorType: p->append(SkRasterPipeline::gather_1010102, ctx);
447 p->append(SkRasterPipeline::swap_rb ); break;
448
Mike Kleinac568a92018-01-25 09:09:32 -0500449 case kRGB_101010x_SkColorType: p->append(SkRasterPipeline::gather_1010102, ctx);
450 p->append(SkRasterPipeline::force_opaque ); break;
451
Mike Kleinf7eb0542020-02-11 12:19:08 -0600452 case kBGR_101010x_SkColorType: p->append(SkRasterPipeline::gather_1010102, ctx);
453 p->append(SkRasterPipeline::force_opaque );
454 p->append(SkRasterPipeline::swap_rb ); break;
455
Mike Klein1a3eb522018-10-18 10:11:00 -0400456 case kBGRA_8888_SkColorType: p->append(SkRasterPipeline::gather_8888, ctx);
457 p->append(SkRasterPipeline::swap_rb ); break;
458
Mike Kleinb70990e2019-02-28 10:03:27 -0600459 case kUnknown_SkColorType: SkASSERT(false);
Mike Kleinb04c3522016-11-28 11:55:58 -0500460 }
Mike Reeddfc0e912018-02-16 12:40:18 -0500461 if (decal_ctx) {
462 p->append(SkRasterPipeline::check_decal_mask, decal_ctx);
463 }
Mike Kleinf7f883b2016-11-21 15:09:45 -0500464 };
465
Mike Klein1fa9c432017-12-11 09:59:47 -0500466 auto append_misc = [&] {
Mike Klein059e0432020-01-02 16:32:38 -0600467 // This is an inessential optimization... it's logically safe to set this to false.
468 // But if...
469 // - we know the image is definitely normalized, and
470 // - we're doing some color space conversion, and
471 // - sRGB curves are involved,
472 // then we can use slightly faster math that doesn't work well outside [0,1].
473 bool src_is_normalized = SkColorTypeIsNormalized(info.colorType());
474
Mike Klein6a9f1c42020-01-02 23:23:42 -0600475 SkColorSpace* cs = info.colorSpace();
476 SkAlphaType at = info.alphaType();
477
478 // Color for A8 images comes from the paint. TODO: all alpha images? none?
479 if (info.colorType() == kAlpha_8_SkColorType) {
480 SkColor4f rgb = rec.fPaint.getColor4f();
481 p->append_set_rgb(alloc, rgb);
482
483 src_is_normalized = rgb.fitsInBytes();
484 cs = sk_srgb_singleton();
485 at = kUnpremul_SkAlphaType;
486 }
487
Mike Klein059e0432020-01-02 16:32:38 -0600488 // Bicubic filtering naturally produces out of range values on both sides of [0,1].
Mike Klein250138d2020-01-02 09:39:26 -0600489 if (quality == kHigh_SkFilterQuality) {
Mike Klein1fa9c432017-12-11 09:59:47 -0500490 p->append(SkRasterPipeline::clamp_0);
Mike Klein059e0432020-01-02 16:32:38 -0600491 p->append(at == kUnpremul_SkAlphaType || fClampAsIfUnpremul
492 ? SkRasterPipeline::clamp_1
493 : SkRasterPipeline::clamp_a);
494 src_is_normalized = true;
Mike Klein1fa9c432017-12-11 09:59:47 -0500495 }
Mike Kleinb82edcc2018-07-10 18:25:03 +0000496
Mike Klein059e0432020-01-02 16:32:38 -0600497 // Transform color space and alpha type to match shader convention (dst CS, premul alpha).
498 alloc->make<SkColorSpaceXformSteps>(cs, at,
499 rec.fDstCS, kPremul_SkAlphaType)
500 ->apply(p, src_is_normalized);
Mike Kleinb82edcc2018-07-10 18:25:03 +0000501
Mike Klein1fa9c432017-12-11 09:59:47 -0500502 return true;
503 };
504
Mike Reed9318a6c2019-08-16 16:16:25 -0400505 // Check for fast-path stages.
Mike Klein8e3426f2018-04-16 12:56:24 -0400506 auto ct = info.colorType();
507 if (true
508 && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType)
509 && quality == kLow_SkFilterQuality
Mike Reedfae8fce2019-04-03 10:27:45 -0400510 && fTileModeX == SkTileMode::kClamp && fTileModeY == SkTileMode::kClamp) {
Mike Klein1fa9c432017-12-11 09:59:47 -0500511
512 p->append(SkRasterPipeline::bilerp_clamp_8888, gather);
Mike Klein8e3426f2018-04-16 12:56:24 -0400513 if (ct == kBGRA_8888_SkColorType) {
514 p->append(SkRasterPipeline::swap_rb);
515 }
Mike Klein1fa9c432017-12-11 09:59:47 -0500516 return append_misc();
517 }
Mike Reed78eedba2019-07-31 16:39:15 -0400518 if (true
Mike Klein01005622019-08-13 12:22:17 -0400519 && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType) // TODO: all formats
520 && quality == kLow_SkFilterQuality
521 && fTileModeX != SkTileMode::kDecal // TODO decal too?
522 && fTileModeY != SkTileMode::kDecal) {
523
524 auto ctx = alloc->make<SkRasterPipeline_SamplerCtx2>();
525 *(SkRasterPipeline_GatherCtx*)(ctx) = *gather;
526 ctx->ct = ct;
527 ctx->tileX = fTileModeX;
528 ctx->tileY = fTileModeY;
529 ctx->invWidth = 1.0f / ctx->width;
530 ctx->invHeight = 1.0f / ctx->height;
531 p->append(SkRasterPipeline::bilinear, ctx);
532 return append_misc();
533 }
534 if (true
Mike Reed78eedba2019-07-31 16:39:15 -0400535 && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType)
536 && quality == kHigh_SkFilterQuality
537 && fTileModeX == SkTileMode::kClamp && fTileModeY == SkTileMode::kClamp) {
538
539 p->append(SkRasterPipeline::bicubic_clamp_8888, gather);
540 if (ct == kBGRA_8888_SkColorType) {
541 p->append(SkRasterPipeline::swap_rb);
542 }
543 return append_misc();
544 }
Mike Klein01005622019-08-13 12:22:17 -0400545 if (true
546 && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType) // TODO: all formats
547 && quality == kHigh_SkFilterQuality
548 && fTileModeX != SkTileMode::kDecal // TODO decal too?
549 && fTileModeY != SkTileMode::kDecal) {
550
551 auto ctx = alloc->make<SkRasterPipeline_SamplerCtx2>();
552 *(SkRasterPipeline_GatherCtx*)(ctx) = *gather;
553 ctx->ct = ct;
554 ctx->tileX = fTileModeX;
555 ctx->tileY = fTileModeY;
556 ctx->invWidth = 1.0f / ctx->width;
557 ctx->invHeight = 1.0f / ctx->height;
558 p->append(SkRasterPipeline::bicubic, ctx);
559 return append_misc();
560 }
Mike Klein1fa9c432017-12-11 09:59:47 -0500561
Mike Kleinb11ab572018-10-24 06:42:14 -0400562 SkRasterPipeline_SamplerCtx* sampler = nullptr;
Mike Klein0a904492017-04-12 12:52:48 -0400563 if (quality != kNone_SkFilterQuality) {
Mike Kleinb11ab572018-10-24 06:42:14 -0400564 sampler = alloc->make<SkRasterPipeline_SamplerCtx>();
Mike Klein0a904492017-04-12 12:52:48 -0400565 }
566
Mike Kleinb0b17d12016-12-09 16:25:44 -0500567 auto sample = [&](SkRasterPipeline::StockStage setup_x,
568 SkRasterPipeline::StockStage setup_y) {
Mike Klein0a904492017-04-12 12:52:48 -0400569 p->append(setup_x, sampler);
570 p->append(setup_y, sampler);
Mike Klein886cf532016-12-06 11:31:25 -0500571 append_tiling_and_gather();
Mike Klein0a904492017-04-12 12:52:48 -0400572 p->append(SkRasterPipeline::accumulate, sampler);
Mike Klein886cf532016-12-06 11:31:25 -0500573 };
574
Mike Kleinf7f883b2016-11-21 15:09:45 -0500575 if (quality == kNone_SkFilterQuality) {
Mike Kleinb04c3522016-11-28 11:55:58 -0500576 append_tiling_and_gather();
Mike Kleinb0b17d12016-12-09 16:25:44 -0500577 } else if (quality == kLow_SkFilterQuality) {
Mike Klein0a904492017-04-12 12:52:48 -0400578 p->append(SkRasterPipeline::save_xy, sampler);
Mike Kleinb0b17d12016-12-09 16:25:44 -0500579
580 sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_ny);
581 sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_ny);
582 sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_py);
583 sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_py);
584
585 p->append(SkRasterPipeline::move_dst_src);
Mike Klein1fa9c432017-12-11 09:59:47 -0500586
Mike Klein46e66a22016-11-21 16:19:34 -0500587 } else {
Mike Klein250138d2020-01-02 09:39:26 -0600588 SkASSERT(quality == kHigh_SkFilterQuality);
Mike Klein0a904492017-04-12 12:52:48 -0400589 p->append(SkRasterPipeline::save_xy, sampler);
Mike Kleinb0b17d12016-12-09 16:25:44 -0500590
591 sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n3y);
592 sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n3y);
593 sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n3y);
594 sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n3y);
595
596 sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n1y);
597 sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n1y);
598 sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n1y);
599 sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n1y);
600
601 sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p1y);
602 sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p1y);
603 sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p1y);
604 sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p1y);
605
606 sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p3y);
607 sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p3y);
608 sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p3y);
609 sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p3y);
610
Mike Kleinb04c3522016-11-28 11:55:58 -0500611 p->append(SkRasterPipeline::move_dst_src);
Mike Klein06a65e22016-11-17 12:39:09 -0500612 }
613
Mike Klein1fa9c432017-12-11 09:59:47 -0500614 return append_misc();
Mike Klein06a65e22016-11-17 12:39:09 -0500615}
Mike Reed9318a6c2019-08-16 16:16:25 -0400616
617bool SkImageShader::onAppendStages(const SkStageRec& rec) const {
618 return this->doStages(rec, nullptr);
619}
620
621SkStageUpdater* SkImageShader::onAppendUpdatableStages(const SkStageRec& rec) const {
Mike Reed8845c372019-12-19 13:22:08 -0500622 bool usePersp = rec.fCTM.hasPerspective();
623 auto updater = rec.fAlloc->make<SkImageStageUpdater>(this, usePersp);
Mike Reed9318a6c2019-08-16 16:16:25 -0400624 return this->doStages(rec, updater) ? updater : nullptr;
625}
626
Mike Kleina434e0f2020-03-23 09:33:48 -0500627skvm::Color SkImageShader::onProgram(skvm::Builder* p, skvm::F32 x, skvm::F32 y, skvm::Color paint,
Mike Reed6352f002020-03-14 23:30:10 -0400628 const SkMatrix& ctm, const SkMatrix* localM,
Mike Kleina434e0f2020-03-23 09:33:48 -0500629 SkFilterQuality quality, const SkColorInfo& dst,
Mike Klein276a7852020-03-15 08:46:09 -0500630 skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
Mike Kleinf6a715b2019-12-30 15:24:18 -0600631 SkMatrix inv;
632 if (!this->computeTotalInverse(ctm, localM, &inv)) {
Mike Reed6352f002020-03-14 23:30:10 -0400633 return {};
Mike Kleinf6a715b2019-12-30 15:24:18 -0600634 }
635
Mike Klein3ab71282020-01-06 19:53:39 -0600636 // We use RequestBitmap() to make sure our SkBitmapController::State lives in the alloc.
637 // This lets the SkVMBlitter hang on to this state and keep our image alive.
638 auto state = SkBitmapController::RequestBitmap(as_IB(fImage.get()), inv, quality, alloc);
639 if (!state) {
Mike Reed6352f002020-03-14 23:30:10 -0400640 return {};
Mike Kleinf6a715b2019-12-30 15:24:18 -0600641 }
Mike Klein3ab71282020-01-06 19:53:39 -0600642 const SkPixmap& pm = state->pixmap();
643 inv = state->invMatrix();
644 quality = state->quality();
Mike Klein6dbd7ff2020-01-06 11:50:37 -0600645 tweak_quality_and_inv_matrix(&quality, &inv);
Mike Klein85754d52020-01-22 10:04:11 -0600646 inv.normalizePerspective();
Mike Kleinf6a715b2019-12-30 15:24:18 -0600647
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600648 // Apply matrix to convert dst coords to sample center coords.
Mike Klein85754d52020-01-22 10:04:11 -0600649 SkShaderBase::ApplyMatrix(p, inv, &x,&y,uniforms);
Mike Klein6dbd7ff2020-01-06 11:50:37 -0600650
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600651 // Bail out if sample() can't yet handle our image's color type.
Mike Klein25480bf2020-01-06 14:01:58 -0600652 switch (pm.colorType()) {
Mike Reed6352f002020-03-14 23:30:10 -0400653 default: return {};
Mike Klein913a6f52020-03-23 11:06:25 -0500654 case kGray_8_SkColorType:
655 case kAlpha_8_SkColorType:
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600656 case kRGB_565_SkColorType:
Mike Klein748b7572020-02-12 11:21:11 -0600657 case kRGB_888x_SkColorType:
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600658 case kRGBA_8888_SkColorType:
Mike Klein748b7572020-02-12 11:21:11 -0600659 case kBGRA_8888_SkColorType:
660 case kRGBA_1010102_SkColorType:
661 case kBGRA_1010102_SkColorType:
662 case kRGB_101010x_SkColorType:
663 case kBGR_101010x_SkColorType: break;
Mike Klein25480bf2020-01-06 14:01:58 -0600664 }
665
Mike Klein921236b2020-02-13 11:20:54 -0600666 // We can exploit image opacity to skip work unpacking alpha channels.
667 const bool input_is_opaque = SkAlphaTypeIsOpaque(pm.alphaType())
668 || SkColorTypeIsAlwaysOpaque(pm.colorType());
669
Mike Klein03d89ef2020-01-14 17:18:29 -0600670 // Each call to sample() will try to rewrite the same uniforms over and over,
671 // so remember where we start and reset back there each time. That way each
672 // sample() call uses the same uniform offsets.
673 const size_t uniforms_before_sample = uniforms->buf.size();
674
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600675 auto sample = [&](skvm::F32 sx, skvm::F32 sy) -> skvm::Color {
Mike Klein03d89ef2020-01-14 17:18:29 -0600676 uniforms->buf.resize(uniforms_before_sample);
677
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600678 // repeat() and mirror() are written assuming they'll be followed by a [0,scale) clamp.
679 auto repeat = [&](skvm::F32 v, float scale) {
680 skvm::F32 S = p->uniformF(uniforms->pushF( scale)),
681 I = p->uniformF(uniforms->pushF(1.0f/scale));
Mike Reedf3b9a302020-04-01 13:18:02 -0400682 return v - floor(v * I) * S;
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600683 };
684 auto mirror = [&](skvm::F32 v, float scale) {
685 skvm::F32 S = p->uniformF(uniforms->pushF( scale)),
686 I2 = p->uniformF(uniforms->pushF(0.5f/scale));
687 // abs( (v-scale) - (2*scale)*floor((v-scale)*(0.5f/scale)) - scale )
688 // {---A---} {------------------B------------------}
Mike Reedf3b9a302020-04-01 13:18:02 -0400689 skvm::F32 A = v - S,
690 B = (S + S) * floor(A * I2);
691 return abs(A - B - S);
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600692 };
693 switch (fTileModeX) {
694 case SkTileMode::kDecal: /* handled after gather */ break;
695 case SkTileMode::kClamp: /* we always clamp */ break;
696 case SkTileMode::kRepeat: sx = repeat(sx, pm.width()); break;
697 case SkTileMode::kMirror: sx = mirror(sx, pm.width()); break;
698 }
699 switch (fTileModeY) {
700 case SkTileMode::kDecal: /* handled after gather */ break;
701 case SkTileMode::kClamp: /* we always clamp */ break;
702 case SkTileMode::kRepeat: sy = repeat(sy, pm.height()); break;
703 case SkTileMode::kMirror: sy = mirror(sy, pm.height()); break;
704 }
705
706 // Always clamp sample coordinates to [0,width), [0,height), both for memory
707 // safety and to handle the clamps still needed by kClamp, kRepeat, and kMirror.
Mike Reedf3b9a302020-04-01 13:18:02 -0400708 auto clamp0x = [&](skvm::F32 v, float limit) {
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600709 // Subtract an ulp so the upper clamp limit excludes limit itself.
710 int bits;
711 memcpy(&bits, &limit, 4);
Mike Reedf3b9a302020-04-01 13:18:02 -0400712 return clamp(v, 0.0f, p->uniformF(uniforms->push(bits-1)));
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600713 };
Mike Reedf3b9a302020-04-01 13:18:02 -0400714 skvm::F32 clamped_x = clamp0x(sx, pm. width()),
715 clamped_y = clamp0x(sy, pm.height());
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600716
717 // Load pixels from pm.addr()[(int)sx + (int)sy*stride].
Mike Klein8b99b9e2020-03-31 12:28:41 -0500718 skvm::Uniform img = uniforms->pushPtr(pm.addr());
Mike Reedf3b9a302020-04-01 13:18:02 -0400719 skvm::I32 index = trunc(clamped_x) +
720 trunc(clamped_y) * p->uniform32(uniforms->push(pm.rowBytesAsPixels()));
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600721 skvm::Color c;
722 switch (pm.colorType()) {
723 default: SkUNREACHABLE;
Mike Klein913a6f52020-03-23 11:06:25 -0500724
Mike Reedf3b9a302020-04-01 13:18:02 -0400725 case kGray_8_SkColorType: c.r = c.g = c.b = from_unorm(8, gather8(img, index));
Mike Klein913a6f52020-03-23 11:06:25 -0500726 c.a = p->splat(1.0f);
727 break;
728
729 case kAlpha_8_SkColorType: c.r = c.g = c.b = p->splat(0.0f);
Mike Reedf3b9a302020-04-01 13:18:02 -0400730 c.a = from_unorm(8, gather8(img, index));
Mike Klein913a6f52020-03-23 11:06:25 -0500731 break;
732
Mike Reedf3b9a302020-04-01 13:18:02 -0400733 case kRGB_565_SkColorType: c = unpack_565 (gather16(img, index)); break;
Mike Klein748b7572020-02-12 11:21:11 -0600734
735 case kRGB_888x_SkColorType: [[fallthrough]];
Mike Reedf3b9a302020-04-01 13:18:02 -0400736 case kRGBA_8888_SkColorType: c = unpack_8888(gather32(img, index));
Mike Klein748b7572020-02-12 11:21:11 -0600737 break;
Mike Reedf3b9a302020-04-01 13:18:02 -0400738 case kBGRA_8888_SkColorType: c = unpack_8888(gather32(img, index));
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600739 std::swap(c.r, c.b);
740 break;
Mike Klein748b7572020-02-12 11:21:11 -0600741
742 case kRGB_101010x_SkColorType: [[fallthrough]];
Mike Reedf3b9a302020-04-01 13:18:02 -0400743 case kRGBA_1010102_SkColorType: c = unpack_1010102(gather32(img, index));
Mike Klein748b7572020-02-12 11:21:11 -0600744 break;
745
746 case kBGR_101010x_SkColorType: [[fallthrough]];
Mike Reedf3b9a302020-04-01 13:18:02 -0400747 case kBGRA_1010102_SkColorType: c = unpack_1010102(gather32(img, index));
Mike Klein748b7572020-02-12 11:21:11 -0600748 std::swap(c.r, c.b);
749 break;
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600750 }
Mike Klein921236b2020-02-13 11:20:54 -0600751 // If we know the image is opaque, jump right to alpha = 1.0f, skipping work to unpack it.
752 if (input_is_opaque) {
Mike Klein46874ef2020-02-13 10:17:08 -0600753 c.a = p->splat(1.0f);
754 }
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600755
756 // Mask away any pixels that we tried to sample outside the bounds in kDecal.
757 if (fTileModeX == SkTileMode::kDecal || fTileModeY == SkTileMode::kDecal) {
758 skvm::I32 mask = p->splat(~0);
Mike Reedf3b9a302020-04-01 13:18:02 -0400759 if (fTileModeX == SkTileMode::kDecal) { mask &= (sx == clamped_x); }
760 if (fTileModeY == SkTileMode::kDecal) { mask &= (sy == clamped_y); }
761 c.r = bit_cast(p->bit_and(mask, bit_cast(c.r)));
762 c.g = bit_cast(p->bit_and(mask, bit_cast(c.g)));
763 c.b = bit_cast(p->bit_and(mask, bit_cast(c.b)));
764 c.a = bit_cast(p->bit_and(mask, bit_cast(c.a)));
Mike Klein921236b2020-02-13 11:20:54 -0600765 // Notice that even if input_is_opaque, c.a might now be 0.
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600766 }
767
768 return c;
769 };
770
Mike Reed6352f002020-03-14 23:30:10 -0400771 skvm::Color c;
772
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600773 if (quality == kNone_SkFilterQuality) {
Mike Reed6352f002020-03-14 23:30:10 -0400774 c = sample(x,y);
Mike Klein5c660e02020-01-08 11:36:35 -0600775 } else if (quality == kLow_SkFilterQuality) {
776 // Our four sample points are the corners of a logical 1x1 pixel
777 // box surrounding (x,y) at (0.5,0.5) off-center.
Mike Reedf3b9a302020-04-01 13:18:02 -0400778 skvm::F32 left = x - 0.5f,
779 top = y - 0.5f,
780 right = x + 0.5f,
781 bottom = y + 0.5f;
Mike Klein5c660e02020-01-08 11:36:35 -0600782
783 // The fractional parts of right and bottom are our lerp factors in x and y respectively.
Mike Reedf3b9a302020-04-01 13:18:02 -0400784 skvm::F32 fx = fract(right ),
785 fy = fract(bottom);
Mike Klein5c660e02020-01-08 11:36:35 -0600786
Mike Reedf3b9a302020-04-01 13:18:02 -0400787 c = lerp(lerp(sample(left,top ), sample(right,top ), fx),
788 lerp(sample(left,bottom), sample(right,bottom), fx), fy);
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600789 } else {
Mike Klein5c660e02020-01-08 11:36:35 -0600790 SkASSERT(quality == kHigh_SkFilterQuality);
791
792 // All bicubic samples have the same fractional offset (fx,fy) from the center.
793 // They're either the 16 corners of a 3x3 grid/ surrounding (x,y) at (0.5,0.5) off-center.
Mike Reedf3b9a302020-04-01 13:18:02 -0400794 skvm::F32 fx = fract(x + 0.5f),
795 fy = fract(y + 0.5f);
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600796
Mike Klein5c660e02020-01-08 11:36:35 -0600797 // See GrCubicEffect for details of these weights.
798 // TODO: these maybe don't seem right looking at gm/bicubic and GrBicubicEffect.
799 auto near = [&](skvm::F32 t) {
800 // 1/18 + 9/18t + 27/18t^2 - 21/18t^3 == t ( t ( -21/18t + 27/18) + 9/18) + 1/18
Mike Reedf3b9a302020-04-01 13:18:02 -0400801 return t * (t * (t * (-21/18.0f) + 27/18.0f) + 9/18.0f) + 1/18.0f;
Mike Klein5c660e02020-01-08 11:36:35 -0600802 };
803 auto far = [&](skvm::F32 t) {
804 // 0/18 + 0/18*t - 6/18t^2 + 7/18t^3 == t^2 (7/18t - 6/18)
Mike Reedf3b9a302020-04-01 13:18:02 -0400805 return t * t * (t * (7/18.0f) - 6/18.0f);
Mike Klein5c660e02020-01-08 11:36:35 -0600806 };
807 const skvm::F32 wx[] = {
Mike Reedf3b9a302020-04-01 13:18:02 -0400808 far (1.0f - fx),
809 near(1.0f - fx),
810 near( fx),
811 far ( fx),
Mike Klein5c660e02020-01-08 11:36:35 -0600812 };
813 const skvm::F32 wy[] = {
Mike Reedf3b9a302020-04-01 13:18:02 -0400814 far (1.0f - fy),
815 near(1.0f - fy),
816 near( fy),
817 far ( fy),
Mike Klein5c660e02020-01-08 11:36:35 -0600818 };
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600819
Mike Reed6352f002020-03-14 23:30:10 -0400820 c.r = c.g = c.b = c.a = p->splat(0.0f);
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600821
Mike Reedf3b9a302020-04-01 13:18:02 -0400822 skvm::F32 sy = y - 1.5f;
823 for (int j = 0; j < 4; j++, sy += 1.0f) {
824 skvm::F32 sx = x - 1.5f;
825 for (int i = 0; i < 4; i++, sx += 1.0f) {
Mike Reed6352f002020-03-14 23:30:10 -0400826 skvm::Color s = sample(sx,sy);
Mike Reedf3b9a302020-04-01 13:18:02 -0400827 skvm::F32 w = wx[i] * wy[j];
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600828
Mike Reedf3b9a302020-04-01 13:18:02 -0400829 c.r += s.r * w;
830 c.g += s.g * w;
831 c.b += s.b * w;
832 c.a += s.a * w;
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600833 }
834 }
Mike Klein921236b2020-02-13 11:20:54 -0600835 }
Mike Kleind67a5c32020-01-06 16:01:01 -0600836
Mike Klein921236b2020-02-13 11:20:54 -0600837 // If the input is opaque and we're not in decal mode, that means the output is too.
838 // Forcing *a to 1.0 here will retroactively skip any work we did to interpolate sample alphas.
839 if (input_is_opaque
840 && fTileModeX != SkTileMode::kDecal
841 && fTileModeY != SkTileMode::kDecal) {
Mike Reed6352f002020-03-14 23:30:10 -0400842 c.a = p->splat(1.0f);
Mike Klein921236b2020-02-13 11:20:54 -0600843 }
844
Mike Klein913a6f52020-03-23 11:06:25 -0500845 // Alpha-only images get their color from the paint (already converted to dst color space).
846 SkColorSpace* cs = pm.colorSpace();
847 SkAlphaType at = pm.alphaType();
848 if (SkColorTypeIsAlphaOnly(pm.colorType())) {
849 c.r = paint.r;
850 c.g = paint.g;
851 c.b = paint.b;
852
853 cs = dst.colorSpace();
854 at = kUnpremul_SkAlphaType;
855 }
856
Mike Klein921236b2020-02-13 11:20:54 -0600857 if (quality == kHigh_SkFilterQuality) {
Mike Klein5c660e02020-01-08 11:36:35 -0600858 // Bicubic filtering naturally produces out of range values on both sides of [0,1].
Mike Reedf3b9a302020-04-01 13:18:02 -0400859 c.a = clamp01(c.a);
Mike Klein3f83bfd2020-01-07 12:01:50 -0600860
Mike Klein913a6f52020-03-23 11:06:25 -0500861 skvm::F32 limit = (at == kUnpremul_SkAlphaType || fClampAsIfUnpremul)
Mike Klein3f83bfd2020-01-07 12:01:50 -0600862 ? p->splat(1.0f)
Mike Reed6352f002020-03-14 23:30:10 -0400863 : c.a;
Mike Reedf3b9a302020-04-01 13:18:02 -0400864 c.r = clamp(c.r, 0.0f, limit);
865 c.g = clamp(c.g, 0.0f, limit);
866 c.b = clamp(c.b, 0.0f, limit);
Mike Kleinb1ff79a2020-01-07 07:53:39 -0600867 }
Mike Klein4bc86d52020-01-06 18:39:42 -0600868
Mike Reed0113d292020-03-24 16:36:37 -0400869 SkColorSpaceXformSteps steps{cs,at, dst.colorSpace(),kPremul_SkAlphaType};
870 return steps.program(p, uniforms, c);
Mike Kleinf6a715b2019-12-30 15:24:18 -0600871}
872