bool SkImageShader::appendStages()

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();
}