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