in src/shaders/SkImageShader.cpp [509:800]
bool SkImageShader::appendStages(const SkStageRec& rec, const SkShaders::MatrixRec& mRec) const {
SkASSERT(!needs_subset(fImage.get(), fSubset)); // TODO(skbug.com/12784)
// We only support certain sampling options in stages so far
auto sampling = fSampling;
if (sampling.isAniso()) {
sampling = SkSamplingPriv::AnisoFallback(fImage->hasMipmaps());
}
SkRasterPipeline* p = rec.fPipeline;
SkArenaAlloc* alloc = rec.fAlloc;
SkMatrix baseInv;
// If the total matrix isn't valid then we will always access the base MIP level.
if (mRec.totalMatrixIsValid()) {
if (!mRec.totalInverse(&baseInv)) {
return false;
}
baseInv.normalizePerspective();
}
SkASSERT(!sampling.useCubic || sampling.mipmap == SkMipmapMode::kNone);
auto* access = SkMipmapAccessor::Make(alloc, fImage.get(), baseInv, sampling.mipmap);
if (!access) {
return false;
}
MipLevelHelper upper;
std::tie(upper.pm, upper.inv) = access->level();
if (!sampling.useCubic) {
// TODO: can tweak_sampling sometimes for cubic too when B=0
if (mRec.totalMatrixIsValid()) {
sampling = tweak_sampling(sampling, SkMatrix::Concat(upper.inv, baseInv));
}
}
if (!mRec.apply(rec, upper.inv)) {
return false;
}
upper.allocAndInit(alloc, sampling, fTileModeX, fTileModeY);
MipLevelHelper lower;
SkRasterPipelineContexts::MipmapCtx* mipmapCtx = nullptr;
float lowerWeight = access->lowerWeight();
if (lowerWeight > 0) {
std::tie(lower.pm, lower.inv) = access->lowerLevel();
mipmapCtx = alloc->make<SkRasterPipelineContexts::MipmapCtx>();
mipmapCtx->lowerWeight = lowerWeight;
mipmapCtx->scaleX = static_cast<float>(lower.pm.width()) / upper.pm.width();
mipmapCtx->scaleY = static_cast<float>(lower.pm.height()) / upper.pm.height();
lower.allocAndInit(alloc, sampling, fTileModeX, fTileModeY);
p->append(SkRasterPipelineOp::mipmap_linear_init, mipmapCtx);
}
const bool decalBothAxes = fTileModeX == SkTileMode::kDecal && fTileModeY == SkTileMode::kDecal;
auto append_tiling_and_gather = [&](const MipLevelHelper* level) {
if (decalBothAxes) {
p->append(SkRasterPipelineOp::decal_x_and_y, level->decalCtx);
} else {
switch (fTileModeX) {
case SkTileMode::kClamp: /* The gather_xxx stage will clamp for us. */
break;
case SkTileMode::kMirror:
p->append(SkRasterPipelineOp::mirror_x, level->limitX);
break;
case SkTileMode::kRepeat:
p->append(SkRasterPipelineOp::repeat_x, level->limitX);
break;
case SkTileMode::kDecal:
p->append(SkRasterPipelineOp::decal_x, level->decalCtx);
break;
}
switch (fTileModeY) {
case SkTileMode::kClamp: /* The gather_xxx stage will clamp for us. */
break;
case SkTileMode::kMirror:
p->append(SkRasterPipelineOp::mirror_y, level->limitY);
break;
case SkTileMode::kRepeat:
p->append(SkRasterPipelineOp::repeat_y, level->limitY);
break;
case SkTileMode::kDecal:
p->append(SkRasterPipelineOp::decal_y, level->decalCtx);
break;
}
}
void* ctx = level->gather;
switch (level->pm.colorType()) {
case kAlpha_8_SkColorType: p->append(SkRasterPipelineOp::gather_a8, ctx); break;
case kA16_unorm_SkColorType: p->append(SkRasterPipelineOp::gather_a16, ctx); break;
case kA16_float_SkColorType: p->append(SkRasterPipelineOp::gather_af16, ctx); break;
case kRGB_565_SkColorType: p->append(SkRasterPipelineOp::gather_565, ctx); break;
case kARGB_4444_SkColorType: p->append(SkRasterPipelineOp::gather_4444, ctx); break;
case kR8G8_unorm_SkColorType: p->append(SkRasterPipelineOp::gather_rg88, ctx); break;
case kR16G16_unorm_SkColorType: p->append(SkRasterPipelineOp::gather_rg1616,ctx); break;
case kR16G16_float_SkColorType: p->append(SkRasterPipelineOp::gather_rgf16, ctx); break;
case kRGBA_8888_SkColorType: p->append(SkRasterPipelineOp::gather_8888, ctx); break;
case kRGBA_1010102_SkColorType:
p->append(SkRasterPipelineOp::gather_1010102, ctx);
break;
case kR16G16B16A16_unorm_SkColorType:
p->append(SkRasterPipelineOp::gather_16161616, ctx);
break;
case kRGBA_F16Norm_SkColorType:
case kRGBA_F16_SkColorType: p->append(SkRasterPipelineOp::gather_f16, ctx); break;
case kRGBA_F32_SkColorType: p->append(SkRasterPipelineOp::gather_f32, ctx); break;
case kBGRA_10101010_XR_SkColorType:
p->append(SkRasterPipelineOp::gather_10101010_xr, ctx);
p->append(SkRasterPipelineOp::swap_rb);
break;
case kRGBA_10x6_SkColorType: p->append(SkRasterPipelineOp::gather_10x6, ctx); break;
case kGray_8_SkColorType: p->append(SkRasterPipelineOp::gather_a8, ctx);
p->append(SkRasterPipelineOp::alpha_to_gray ); break;
case kR8_unorm_SkColorType: p->append(SkRasterPipelineOp::gather_a8, ctx);
p->append(SkRasterPipelineOp::alpha_to_red ); break;
case kRGB_888x_SkColorType: p->append(SkRasterPipelineOp::gather_8888, ctx);
p->append(SkRasterPipelineOp::force_opaque ); break;
case kRGB_F16F16F16x_SkColorType:
p->append(SkRasterPipelineOp::gather_f16, ctx);
p->append(SkRasterPipelineOp::force_opaque);
break;
case kBGRA_1010102_SkColorType:
p->append(SkRasterPipelineOp::gather_1010102, ctx);
p->append(SkRasterPipelineOp::swap_rb);
break;
case kRGB_101010x_SkColorType:
p->append(SkRasterPipelineOp::gather_1010102, ctx);
p->append(SkRasterPipelineOp::force_opaque);
break;
case kBGR_101010x_XR_SkColorType:
p->append(SkRasterPipelineOp::gather_1010102_xr, ctx);
p->append(SkRasterPipelineOp::force_opaque);
p->append(SkRasterPipelineOp::swap_rb);
break;
case kBGR_101010x_SkColorType:
p->append(SkRasterPipelineOp::gather_1010102, ctx);
p->append(SkRasterPipelineOp::force_opaque);
p->append(SkRasterPipelineOp::swap_rb);
break;
case kBGRA_8888_SkColorType:
p->append(SkRasterPipelineOp::gather_8888, ctx);
p->append(SkRasterPipelineOp::swap_rb);
break;
case kSRGBA_8888_SkColorType:
p->append(SkRasterPipelineOp::gather_8888, ctx);
p->appendTransferFunction(*skcms_sRGB_TransferFunction());
break;
case kUnknown_SkColorType: SkASSERT(false);
}
if (level->decalCtx) {
p->append(SkRasterPipelineOp::check_decal_mask, level->decalCtx);
}
};
auto append_misc = [&] {
SkColorSpace* cs = upper.pm.colorSpace();
SkAlphaType at = upper.pm.alphaType();
// Color for alpha-only images comes from the paint (already converted to dst color space).
// If we were sampled by a runtime effect, the paint color was replaced with transparent
// black, so this tinting is effectively suppressed. See also: RuntimeEffectRPCallbacks
if (SkColorTypeIsAlphaOnly(upper.pm.colorType()) && !fRaw) {
p->appendSetRGB(alloc, rec.fPaintColor);
cs = rec.fDstCS;
at = kUnpremul_SkAlphaType;
}
// Bicubic filtering naturally produces out of range values on both sides of [0,1].
if (sampling.useCubic) {
p->append(at == kUnpremul_SkAlphaType || fClampAsIfUnpremul
? SkRasterPipelineOp::clamp_01
: SkRasterPipelineOp::clamp_gamut);
}
// Transform color space and alpha type to match shader convention (dst CS, premul alpha).
if (!fRaw) {
alloc->make<SkColorSpaceXformSteps>(cs, at, rec.fDstCS, kPremul_SkAlphaType)->apply(p);
}
return true;
};
// Check for fast-path stages.
// TODO: Could we use the fast-path stages for each level when doing linear mipmap filtering?
SkColorType ct = upper.pm.colorType();
if (true
&& (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType)
&& !sampling.useCubic && sampling.filter == SkFilterMode::kLinear
&& sampling.mipmap != SkMipmapMode::kLinear
&& fTileModeX == SkTileMode::kClamp && fTileModeY == SkTileMode::kClamp) {
p->append(SkRasterPipelineOp::bilerp_clamp_8888, upper.gather);
if (ct == kBGRA_8888_SkColorType) {
p->append(SkRasterPipelineOp::swap_rb);
}
return append_misc();
}
if (true
&& (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType)
&& sampling.useCubic
&& fTileModeX == SkTileMode::kClamp && fTileModeY == SkTileMode::kClamp) {
p->append(SkRasterPipelineOp::bicubic_clamp_8888, upper.gather);
if (ct == kBGRA_8888_SkColorType) {
p->append(SkRasterPipelineOp::swap_rb);
}
return append_misc();
}
// This context can be shared by both levels when doing linear mipmap filtering
SkRasterPipelineContexts::SamplerCtx* sampler =
alloc->make<SkRasterPipelineContexts::SamplerCtx>();
auto sample = [&](SkRasterPipelineOp setup_x,
SkRasterPipelineOp setup_y,
const MipLevelHelper* level) {
p->append(setup_x, sampler);
p->append(setup_y, sampler);
append_tiling_and_gather(level);
p->append(SkRasterPipelineOp::accumulate, sampler);
};
auto sample_level = [&](const MipLevelHelper* level) {
if (sampling.useCubic) {
CubicResamplerMatrix(sampling.cubic.B, sampling.cubic.C).getColMajor(sampler->weights);
p->append(SkRasterPipelineOp::bicubic_setup, sampler);
sample(SkRasterPipelineOp::bicubic_n3x, SkRasterPipelineOp::bicubic_n3y, level);
sample(SkRasterPipelineOp::bicubic_n1x, SkRasterPipelineOp::bicubic_n3y, level);
sample(SkRasterPipelineOp::bicubic_p1x, SkRasterPipelineOp::bicubic_n3y, level);
sample(SkRasterPipelineOp::bicubic_p3x, SkRasterPipelineOp::bicubic_n3y, level);
sample(SkRasterPipelineOp::bicubic_n3x, SkRasterPipelineOp::bicubic_n1y, level);
sample(SkRasterPipelineOp::bicubic_n1x, SkRasterPipelineOp::bicubic_n1y, level);
sample(SkRasterPipelineOp::bicubic_p1x, SkRasterPipelineOp::bicubic_n1y, level);
sample(SkRasterPipelineOp::bicubic_p3x, SkRasterPipelineOp::bicubic_n1y, level);
sample(SkRasterPipelineOp::bicubic_n3x, SkRasterPipelineOp::bicubic_p1y, level);
sample(SkRasterPipelineOp::bicubic_n1x, SkRasterPipelineOp::bicubic_p1y, level);
sample(SkRasterPipelineOp::bicubic_p1x, SkRasterPipelineOp::bicubic_p1y, level);
sample(SkRasterPipelineOp::bicubic_p3x, SkRasterPipelineOp::bicubic_p1y, level);
sample(SkRasterPipelineOp::bicubic_n3x, SkRasterPipelineOp::bicubic_p3y, level);
sample(SkRasterPipelineOp::bicubic_n1x, SkRasterPipelineOp::bicubic_p3y, level);
sample(SkRasterPipelineOp::bicubic_p1x, SkRasterPipelineOp::bicubic_p3y, level);
sample(SkRasterPipelineOp::bicubic_p3x, SkRasterPipelineOp::bicubic_p3y, level);
p->append(SkRasterPipelineOp::move_dst_src);
} else if (sampling.filter == SkFilterMode::kLinear) {
p->append(SkRasterPipelineOp::bilinear_setup, sampler);
sample(SkRasterPipelineOp::bilinear_nx, SkRasterPipelineOp::bilinear_ny, level);
sample(SkRasterPipelineOp::bilinear_px, SkRasterPipelineOp::bilinear_ny, level);
sample(SkRasterPipelineOp::bilinear_nx, SkRasterPipelineOp::bilinear_py, level);
sample(SkRasterPipelineOp::bilinear_px, SkRasterPipelineOp::bilinear_py, level);
p->append(SkRasterPipelineOp::move_dst_src);
} else {
append_tiling_and_gather(level);
}
};
sample_level(&upper);
if (mipmapCtx) {
p->append(SkRasterPipelineOp::mipmap_linear_update, mipmapCtx);
sample_level(&lower);
p->append(SkRasterPipelineOp::mipmap_linear_finish, mipmapCtx);
}
return append_misc();
}