C++程序  |  719行  |  26.38 KB

/*
 * Copyright 2016 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkLinearBitmapPipeline.h"

#include <algorithm>
#include <cmath>
#include <limits>
#include <tuple>

#include "SkArenaAlloc.h"
#include "SkLinearBitmapPipeline_core.h"
#include "SkLinearBitmapPipeline_matrix.h"
#include "SkLinearBitmapPipeline_tile.h"
#include "SkLinearBitmapPipeline_sample.h"
#include "SkNx.h"
#include "SkOpts.h"
#include "SkPM4f.h"

namespace  {

////////////////////////////////////////////////////////////////////////////////////////////////////
// Matrix Stage
// PointProcessor uses a strategy to help complete the work of the different stages. The strategy
// must implement the following methods:
// * processPoints(xs, ys) - must mutate the xs and ys for the stage.
// * maybeProcessSpan(span, next) - This represents a horizontal series of pixels
//   to work over.
//   span - encapsulation of span.
//   next - a pointer to the next stage.
//   maybeProcessSpan - returns false if it can not process the span and needs to fallback to
//                      point lists for processing.
template<typename Strategy, typename Next>
class MatrixStage final : public SkLinearBitmapPipeline::PointProcessorInterface {
public:
    template <typename... Args>
    MatrixStage(Next* next, Args&&... args)
        : fNext{next}
        , fStrategy{std::forward<Args>(args)...}{ }

    MatrixStage(Next* next, MatrixStage* stage)
        : fNext{next}
        , fStrategy{stage->fStrategy} { }

    void SK_VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override {
        fStrategy.processPoints(&xs, &ys);
        fNext->pointListFew(n, xs, ys);
    }

    void SK_VECTORCALL pointList4(Sk4s xs, Sk4s ys) override {
        fStrategy.processPoints(&xs, &ys);
        fNext->pointList4(xs, ys);
    }

    // The span you pass must not be empty.
    void pointSpan(Span span) override {
        SkASSERT(!span.isEmpty());
        if (!fStrategy.maybeProcessSpan(span, fNext)) {
            span_fallback(span, this);
        }
    }

private:
    Next* const fNext;
    Strategy fStrategy;
};

template <typename Next = SkLinearBitmapPipeline::PointProcessorInterface>
using TranslateMatrix = MatrixStage<TranslateMatrixStrategy, Next>;

template <typename Next = SkLinearBitmapPipeline::PointProcessorInterface>
using ScaleMatrix = MatrixStage<ScaleMatrixStrategy, Next>;

template <typename Next = SkLinearBitmapPipeline::PointProcessorInterface>
using AffineMatrix = MatrixStage<AffineMatrixStrategy, Next>;

template <typename Next = SkLinearBitmapPipeline::PointProcessorInterface>
using PerspectiveMatrix = MatrixStage<PerspectiveMatrixStrategy, Next>;


////////////////////////////////////////////////////////////////////////////////////////////////////
// Tile Stage

template<typename XStrategy, typename YStrategy, typename Next>
class CombinedTileStage final : public SkLinearBitmapPipeline::PointProcessorInterface {
public:
    CombinedTileStage(Next* next, SkISize dimensions)
        : fNext{next}
        , fXStrategy{dimensions.width()}
        , fYStrategy{dimensions.height()}{ }

    CombinedTileStage(Next* next, CombinedTileStage* stage)
        : fNext{next}
        , fXStrategy{stage->fXStrategy}
        , fYStrategy{stage->fYStrategy} { }

    void SK_VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override {
        fXStrategy.tileXPoints(&xs);
        fYStrategy.tileYPoints(&ys);
        fNext->pointListFew(n, xs, ys);
    }

    void SK_VECTORCALL pointList4(Sk4s xs, Sk4s ys) override {
        fXStrategy.tileXPoints(&xs);
        fYStrategy.tileYPoints(&ys);
        fNext->pointList4(xs, ys);
    }

    // The span you pass must not be empty.
    void pointSpan(Span span) override {
        SkASSERT(!span.isEmpty());
        SkPoint start; SkScalar length; int count;
        std::tie(start, length, count) = span;

        if (span.count() == 1) {
            // DANGER:
            // The explicit casts from float to Sk4f are not usually necessary, but are here to
            // work around an MSVC 2015u2 c++ code generation bug. This is tracked using skia bug
            // 5566.
            this->pointListFew(1, Sk4f{span.startX()}, Sk4f{span.startY()});
            return;
        }

        SkScalar x = X(start);
        SkScalar y = fYStrategy.tileY(Y(start));
        Span yAdjustedSpan{{x, y}, length, count};

        if (!fXStrategy.maybeProcessSpan(yAdjustedSpan, fNext)) {
            span_fallback(span, this);
        }
    }

private:
    Next* const fNext;
    XStrategy fXStrategy;
    YStrategy fYStrategy;
};

////////////////////////////////////////////////////////////////////////////////////////////////////
// Specialized Samplers

// RGBA8888UnitRepeatSrc - A sampler that takes advantage of the fact the the src and destination
// are the same format and do not need in transformations in pixel space. Therefore, there is no
// need to convert them to HiFi pixel format.
class RGBA8888UnitRepeatSrc final : public SkLinearBitmapPipeline::SampleProcessorInterface,
                                    public SkLinearBitmapPipeline::DestinationInterface {
public:
    RGBA8888UnitRepeatSrc(const uint32_t* src, int32_t width)
        : fSrc{src}, fWidth{width} { }

    void SK_VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override {
        SkASSERT(fDest + n <= fEnd);
        // At this point xs and ys should be >= 0, so trunc is the same as floor.
        Sk4i iXs = SkNx_cast<int>(xs);
        Sk4i iYs = SkNx_cast<int>(ys);

        if (n >= 1) *fDest++ = *this->pixelAddress(iXs[0], iYs[0]);
        if (n >= 2) *fDest++ = *this->pixelAddress(iXs[1], iYs[1]);
        if (n >= 3) *fDest++ = *this->pixelAddress(iXs[2], iYs[2]);
    }

    void SK_VECTORCALL pointList4(Sk4s xs, Sk4s ys) override {
        SkASSERT(fDest + 4 <= fEnd);
        Sk4i iXs = SkNx_cast<int>(xs);
        Sk4i iYs = SkNx_cast<int>(ys);
        *fDest++ = *this->pixelAddress(iXs[0], iYs[0]);
        *fDest++ = *this->pixelAddress(iXs[1], iYs[1]);
        *fDest++ = *this->pixelAddress(iXs[2], iYs[2]);
        *fDest++ = *this->pixelAddress(iXs[3], iYs[3]);
    }

    void pointSpan(Span span) override {
        SkASSERT(fDest + span.count() <= fEnd);
        if (span.length() != 0.0f) {
            int32_t x = SkScalarTruncToInt(span.startX());
            int32_t y = SkScalarTruncToInt(span.startY());
            const uint32_t* src = this->pixelAddress(x, y);
            memmove(fDest, src, span.count() * sizeof(uint32_t));
            fDest += span.count();
        }
    }

    void repeatSpan(Span span, int32_t repeatCount) override {
        SkASSERT(fDest + span.count() * repeatCount <= fEnd);

        int32_t x = SkScalarTruncToInt(span.startX());
        int32_t y = SkScalarTruncToInt(span.startY());
        const uint32_t* src = this->pixelAddress(x, y);
        uint32_t* dest = fDest;
        while (repeatCount --> 0) {
            memmove(dest, src, span.count() * sizeof(uint32_t));
            dest += span.count();
        }
        fDest = dest;
    }

    void setDestination(void* dst, int count) override  {
        fDest = static_cast<uint32_t*>(dst);
        fEnd = fDest + count;
    }

private:
    const uint32_t* pixelAddress(int32_t x, int32_t y) {
        return &fSrc[fWidth * y + x];
    }
    const uint32_t* const fSrc;
    const int32_t         fWidth;
    uint32_t*             fDest;
    uint32_t*             fEnd;
};

// RGBA8888UnitRepeatSrc - A sampler that takes advantage of the fact the the src and destination
// are the same format and do not need in transformations in pixel space. Therefore, there is no
// need to convert them to HiFi pixel format.
class RGBA8888UnitRepeatSrcOver final : public SkLinearBitmapPipeline::SampleProcessorInterface,
                                        public SkLinearBitmapPipeline::DestinationInterface {
public:
    RGBA8888UnitRepeatSrcOver(const uint32_t* src, int32_t width)
        : fSrc{src}, fWidth{width} { }

    void SK_VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override {
        SkASSERT(fDest + n <= fEnd);
        // At this point xs and ys should be >= 0, so trunc is the same as floor.
        Sk4i iXs = SkNx_cast<int>(xs);
        Sk4i iYs = SkNx_cast<int>(ys);

        if (n >= 1) blendPixelAt(iXs[0], iYs[0]);
        if (n >= 2) blendPixelAt(iXs[1], iYs[1]);
        if (n >= 3) blendPixelAt(iXs[2], iYs[2]);
    }

    void SK_VECTORCALL pointList4(Sk4s xs, Sk4s ys) override {
        SkASSERT(fDest + 4 <= fEnd);
        Sk4i iXs = SkNx_cast<int>(xs);
        Sk4i iYs = SkNx_cast<int>(ys);
        blendPixelAt(iXs[0], iYs[0]);
        blendPixelAt(iXs[1], iYs[1]);
        blendPixelAt(iXs[2], iYs[2]);
        blendPixelAt(iXs[3], iYs[3]);
    }

    void pointSpan(Span span) override {
        if (span.length() != 0.0f) {
            this->repeatSpan(span, 1);
        }
    }

    void repeatSpan(Span span, int32_t repeatCount) override {
        SkASSERT(fDest + span.count() * repeatCount <= fEnd);
        SkASSERT(span.count() > 0);
        SkASSERT(repeatCount > 0);

        int32_t x = (int32_t)span.startX();
        int32_t y = (int32_t)span.startY();
        const uint32_t* beginSpan = this->pixelAddress(x, y);

        SkOpts::srcover_srgb_srgb(fDest, beginSpan, span.count() * repeatCount, span.count());

        fDest += span.count() * repeatCount;

        SkASSERT(fDest <= fEnd);
    }

    void setDestination(void* dst, int count) override  {
        SkASSERT(count > 0);
        fDest = static_cast<uint32_t*>(dst);
        fEnd = fDest + count;
    }

private:
    const uint32_t* pixelAddress(int32_t x, int32_t y) {
        return &fSrc[fWidth * y + x];
    }

    void blendPixelAt(int32_t x, int32_t y) {
        const uint32_t* src = this->pixelAddress(x, y);
        SkOpts::srcover_srgb_srgb(fDest, src, 1, 1);
        fDest += 1;
    }

    const uint32_t* const fSrc;
    const int32_t         fWidth;
    uint32_t*             fDest;
    uint32_t*             fEnd;
};

using Blender = SkLinearBitmapPipeline::BlendProcessorInterface;

////////////////////////////////////////////////////////////////////////////////////////////////////
// Pixel Blender Stage
template <SkAlphaType alphaType>
class SrcFPPixel final : public Blender {
public:
    SrcFPPixel(float postAlpha) : fPostAlpha{postAlpha} { }
    SrcFPPixel(const SrcFPPixel& Blender) : fPostAlpha(Blender.fPostAlpha) {}
    void SK_VECTORCALL blendPixel(Sk4f pixel) override {
        SkASSERT(fDst + 1 <= fEnd );
        this->srcPixel(fDst, pixel, 0);
        fDst += 1;
    }

    void SK_VECTORCALL blend4Pixels(Sk4f p0, Sk4f p1, Sk4f p2, Sk4f p3) override {
        SkASSERT(fDst + 4 <= fEnd);
        SkPM4f* dst = fDst;
        this->srcPixel(dst, p0, 0);
        this->srcPixel(dst, p1, 1);
        this->srcPixel(dst, p2, 2);
        this->srcPixel(dst, p3, 3);
        fDst += 4;
    }

    void setDestination(void* dst, int count) override {
        fDst = static_cast<SkPM4f*>(dst);
        fEnd = fDst + count;
    }

private:
    void SK_VECTORCALL srcPixel(SkPM4f* dst, Sk4f pixel, int index) {
        check_pixel(pixel);

        Sk4f newPixel = pixel;
        if (alphaType == kUnpremul_SkAlphaType) {
            newPixel = Premultiply(pixel);
        }
        newPixel = newPixel * fPostAlpha;
        newPixel.store(dst + index);
    }
    static Sk4f SK_VECTORCALL Premultiply(Sk4f pixel) {
        float alpha = pixel[3];
        return pixel * Sk4f{alpha, alpha, alpha, 1.0f};
    }

    SkPM4f* fDst;
    SkPM4f* fEnd;
    float   fPostAlpha;
};

}  // namespace

////////////////////////////////////////////////////////////////////////////////////////////////////
// SkLinearBitmapPipeline
SkLinearBitmapPipeline::~SkLinearBitmapPipeline() {}

SkLinearBitmapPipeline::SkLinearBitmapPipeline(
    const SkMatrix& inverse,
    SkFilterQuality filterQuality,
    SkShader::TileMode xTile, SkShader::TileMode yTile,
    SkColor paintColor,
    const SkPixmap& srcPixmap,
    SkArenaAlloc* allocator)
{
    SkISize dimensions = srcPixmap.info().dimensions();
    const SkImageInfo& srcImageInfo = srcPixmap.info();

    SkMatrix adjustedInverse = inverse;
    if (filterQuality == kNone_SkFilterQuality) {
        if (inverse.getScaleX() >= 0.0f) {
            adjustedInverse.setTranslateX(
                nextafterf(inverse.getTranslateX(), std::floor(inverse.getTranslateX())));
        }
        if (inverse.getScaleY() >= 0.0f) {
            adjustedInverse.setTranslateY(
                nextafterf(inverse.getTranslateY(), std::floor(inverse.getTranslateY())));
        }
    }

    SkScalar dx = adjustedInverse.getScaleX();

    // If it is an index 8 color type, the sampler converts to unpremul for better fidelity.
    SkAlphaType alphaType = srcImageInfo.alphaType();
    if (srcPixmap.colorType() == kIndex_8_SkColorType) {
        alphaType = kUnpremul_SkAlphaType;
    }

    float postAlpha = SkColorGetA(paintColor) * (1.0f / 255.0f);
    // As the stages are built, the chooser function may skip a stage. For example, with the
    // identity matrix, the matrix stage is skipped, and the tilerStage is the first stage.
    auto blenderStage = this->chooseBlenderForShading(alphaType, postAlpha, allocator);
    auto samplerStage = this->chooseSampler(
        blenderStage, filterQuality, xTile, yTile, srcPixmap, paintColor, allocator);
    auto tilerStage   = this->chooseTiler(
        samplerStage, dimensions, xTile, yTile, filterQuality, dx, allocator);
    fFirstStage       = this->chooseMatrix(tilerStage, adjustedInverse, allocator);
    fLastStage        = blenderStage;
}

SkLinearBitmapPipeline::SkLinearBitmapPipeline(
    const SkLinearBitmapPipeline& pipeline,
    const SkPixmap& srcPixmap,
    SkBlendMode mode,
    const SkImageInfo& dstInfo,
    SkArenaAlloc* allocator)
{
    SkASSERT(mode == SkBlendMode::kSrc || mode == SkBlendMode::kSrcOver);
    SkASSERT(srcPixmap.info().colorType() == dstInfo.colorType()
             && srcPixmap.info().colorType() == kRGBA_8888_SkColorType);

    SampleProcessorInterface* sampleStage;
    if (mode == SkBlendMode::kSrc) {
        auto sampler = allocator->make<RGBA8888UnitRepeatSrc>(
            srcPixmap.writable_addr32(0, 0), srcPixmap.rowBytes() / 4);
        sampleStage = sampler;
        fLastStage = sampler;
    } else {
        auto sampler = allocator->make<RGBA8888UnitRepeatSrcOver>(
            srcPixmap.writable_addr32(0, 0), srcPixmap.rowBytes() / 4);
        sampleStage = sampler;
        fLastStage = sampler;
    }

    auto tilerStage = pipeline.fTileStageCloner(sampleStage, allocator);
    auto matrixStage = pipeline.fMatrixStageCloner(tilerStage, allocator);
    fFirstStage = matrixStage;
}

SkLinearBitmapPipeline* SkLinearBitmapPipeline::ClonePipelineForBlitting(
    const SkLinearBitmapPipeline& pipeline,
    SkMatrix::TypeMask matrixMask,
    SkFilterQuality filterQuality,
    const SkPixmap& srcPixmap,
    float finalAlpha,
    SkBlendMode blendMode,
    const SkImageInfo& dstInfo,
    SkArenaAlloc* allocator)
{
    if (blendMode == SkBlendMode::kSrcOver && srcPixmap.info().alphaType() == kOpaque_SkAlphaType) {
        blendMode = SkBlendMode::kSrc;
    }

    if (matrixMask & ~SkMatrix::kTranslate_Mask ) { return nullptr; }
    if (filterQuality != SkFilterQuality::kNone_SkFilterQuality) { return nullptr; }
    if (finalAlpha != 1.0f) { return nullptr; }
    if (srcPixmap.info().colorType() != kRGBA_8888_SkColorType
        || dstInfo.colorType() != kRGBA_8888_SkColorType) { return nullptr; }

    if (!srcPixmap.info().gammaCloseToSRGB() || !dstInfo.gammaCloseToSRGB()) {
        return nullptr;
    }

    if (blendMode != SkBlendMode::kSrc && blendMode != SkBlendMode::kSrcOver) {
        return nullptr;
    }

    return allocator->make<SkLinearBitmapPipeline>(
        pipeline, srcPixmap, blendMode, dstInfo, allocator);
}

void SkLinearBitmapPipeline::shadeSpan4f(int x, int y, SkPM4f* dst, int count) {
    SkASSERT(count > 0);
    this->blitSpan(x, y, dst, count);
}

void SkLinearBitmapPipeline::blitSpan(int x, int y, void* dst, int count) {
    SkASSERT(count > 0);
    fLastStage->setDestination(dst, count);

    // The count and length arguments start out in a precise relation in order to keep the
    // math correct through the different stages. Count is the number of pixel to produce.
    // Since the code samples at pixel centers, length is the distance from the center of the
    // first pixel to the center of the last pixel. This implies that length is count-1.
    fFirstStage->pointSpan(Span{{x + 0.5f, y + 0.5f}, count - 1.0f, count});
}

SkLinearBitmapPipeline::PointProcessorInterface*
SkLinearBitmapPipeline::chooseMatrix(
    PointProcessorInterface* next,
    const SkMatrix& inverse,
    SkArenaAlloc* allocator)
{
    if (inverse.hasPerspective()) {
        auto matrixStage = allocator->make<PerspectiveMatrix<>>(
            next,
            SkVector{inverse.getTranslateX(), inverse.getTranslateY()},
            SkVector{inverse.getScaleX(), inverse.getScaleY()},
            SkVector{inverse.getSkewX(), inverse.getSkewY()},
            SkVector{inverse.getPerspX(), inverse.getPerspY()},
            inverse.get(SkMatrix::kMPersp2));
        fMatrixStageCloner =
            [matrixStage](PointProcessorInterface* cloneNext, SkArenaAlloc* memory) {
                return memory->make<PerspectiveMatrix<>>(cloneNext, matrixStage);
            };
        return matrixStage;
    } else if (inverse.getSkewX() != 0.0f || inverse.getSkewY() != 0.0f) {
        auto matrixStage = allocator->make<AffineMatrix<>>(
            next,
            SkVector{inverse.getTranslateX(), inverse.getTranslateY()},
            SkVector{inverse.getScaleX(), inverse.getScaleY()},
            SkVector{inverse.getSkewX(), inverse.getSkewY()});
        fMatrixStageCloner =
            [matrixStage](PointProcessorInterface* cloneNext, SkArenaAlloc* memory) {
                return memory->make<AffineMatrix<>>(cloneNext, matrixStage);
            };
        return matrixStage;
    } else if (inverse.getScaleX() != 1.0f || inverse.getScaleY() != 1.0f) {
        auto matrixStage = allocator->make<ScaleMatrix<>>(
            next,
            SkVector{inverse.getTranslateX(), inverse.getTranslateY()},
            SkVector{inverse.getScaleX(), inverse.getScaleY()});
        fMatrixStageCloner =
            [matrixStage](PointProcessorInterface* cloneNext, SkArenaAlloc* memory) {
                return memory->make<ScaleMatrix<>>(cloneNext, matrixStage);
            };
        return matrixStage;
    } else if (inverse.getTranslateX() != 0.0f || inverse.getTranslateY() != 0.0f) {
        auto matrixStage = allocator->make<TranslateMatrix<>>(
            next,
            SkVector{inverse.getTranslateX(), inverse.getTranslateY()});
        fMatrixStageCloner =
            [matrixStage](PointProcessorInterface* cloneNext, SkArenaAlloc* memory) {
                return memory->make<TranslateMatrix<>>(cloneNext, matrixStage);
            };
        return matrixStage;
    } else {
        fMatrixStageCloner = [](PointProcessorInterface* cloneNext, SkArenaAlloc* memory) {
            return cloneNext;
        };
        return next;
    }
}

template <typename Tiler>
SkLinearBitmapPipeline::PointProcessorInterface* SkLinearBitmapPipeline::createTiler(
    SampleProcessorInterface* next,
    SkISize dimensions,
    SkArenaAlloc* allocator)
{
    auto tilerStage = allocator->make<Tiler>(next, dimensions);
    fTileStageCloner =
        [tilerStage](SampleProcessorInterface* cloneNext,
                     SkArenaAlloc* memory) -> PointProcessorInterface* {
            return memory->make<Tiler>(cloneNext, tilerStage);
        };
    return tilerStage;
}

template <typename XStrategy>
SkLinearBitmapPipeline::PointProcessorInterface* SkLinearBitmapPipeline::chooseTilerYMode(
    SampleProcessorInterface* next,
    SkShader::TileMode yMode,
    SkISize dimensions,
    SkArenaAlloc* allocator)
{
    switch (yMode) {
        case SkShader::kClamp_TileMode: {
            using Tiler = CombinedTileStage<XStrategy, YClampStrategy, SampleProcessorInterface>;
            return this->createTiler<Tiler>(next, dimensions, allocator);
        }
        case SkShader::kRepeat_TileMode: {
            using Tiler = CombinedTileStage<XStrategy, YRepeatStrategy, SampleProcessorInterface>;
            return this->createTiler<Tiler>(next, dimensions, allocator);
        }
        case SkShader::kMirror_TileMode: {
            using Tiler = CombinedTileStage<XStrategy, YMirrorStrategy, SampleProcessorInterface>;
            return this->createTiler<Tiler>(next, dimensions, allocator);
        }
    }

    // Should never get here.
    SkFAIL("Not all Y tile cases covered.");
    return nullptr;
}

SkLinearBitmapPipeline::PointProcessorInterface* SkLinearBitmapPipeline::chooseTiler(
    SampleProcessorInterface* next,
    SkISize dimensions,
    SkShader::TileMode xMode,
    SkShader::TileMode yMode,
    SkFilterQuality filterQuality,
    SkScalar dx,
    SkArenaAlloc* allocator)
{
    switch (xMode) {
        case SkShader::kClamp_TileMode:
            return this->chooseTilerYMode<XClampStrategy>(next, yMode, dimensions, allocator);
        case SkShader::kRepeat_TileMode:
            if (dx == 1.0f && filterQuality == kNone_SkFilterQuality) {
                return this->chooseTilerYMode<XRepeatUnitScaleStrategy>(
                    next, yMode, dimensions, allocator);
            } else {
                return this->chooseTilerYMode<XRepeatStrategy>(
                    next, yMode, dimensions, allocator);
            }
        case SkShader::kMirror_TileMode:
            return this->chooseTilerYMode<XMirrorStrategy>(next, yMode, dimensions, allocator);
    }

    // Should never get here.
    SkFAIL("Not all X tile cases covered.");
    return nullptr;
}

template <SkColorType colorType>
SkLinearBitmapPipeline::PixelAccessorInterface*
    SkLinearBitmapPipeline::chooseSpecificAccessor(
    const SkPixmap& srcPixmap,
    SkArenaAlloc* allocator)
{
    if (srcPixmap.info().gammaCloseToSRGB()) {
        using Accessor = PixelAccessor<colorType, kSRGB_SkGammaType>;
        return allocator->make<Accessor>(srcPixmap);
    } else {
        using Accessor = PixelAccessor<colorType, kLinear_SkGammaType>;
        return allocator->make<Accessor>(srcPixmap);
    }
}

SkLinearBitmapPipeline::PixelAccessorInterface* SkLinearBitmapPipeline::choosePixelAccessor(
    const SkPixmap& srcPixmap,
    const SkColor A8TintColor,
    SkArenaAlloc* allocator)
{
    const SkImageInfo& imageInfo = srcPixmap.info();

    switch (imageInfo.colorType()) {
        case kAlpha_8_SkColorType: {
            using Accessor = PixelAccessor<kAlpha_8_SkColorType, kLinear_SkGammaType>;
            return allocator->make<Accessor>(srcPixmap, A8TintColor);
        }
        case kARGB_4444_SkColorType:
            return this->chooseSpecificAccessor<kARGB_4444_SkColorType>(srcPixmap, allocator);
        case kRGB_565_SkColorType:
            return this->chooseSpecificAccessor<kRGB_565_SkColorType>(srcPixmap, allocator);
        case kRGBA_8888_SkColorType:
            return this->chooseSpecificAccessor<kRGBA_8888_SkColorType>(srcPixmap, allocator);
        case kBGRA_8888_SkColorType:
            return this->chooseSpecificAccessor<kBGRA_8888_SkColorType>(srcPixmap, allocator);
        case kIndex_8_SkColorType:
            return this->chooseSpecificAccessor<kIndex_8_SkColorType>(srcPixmap, allocator);
        case kGray_8_SkColorType:
            return this->chooseSpecificAccessor<kGray_8_SkColorType>(srcPixmap, allocator);
        case kRGBA_F16_SkColorType: {
            using Accessor = PixelAccessor<kRGBA_F16_SkColorType, kLinear_SkGammaType>;
            return allocator->make<Accessor>(srcPixmap);
        }
        default:
            // Should never get here.
            SkFAIL("Pixel source not supported.");
            return nullptr;
    }
}

SkLinearBitmapPipeline::SampleProcessorInterface* SkLinearBitmapPipeline::chooseSampler(
    Blender* next,
    SkFilterQuality filterQuality,
    SkShader::TileMode xTile, SkShader::TileMode yTile,
    const SkPixmap& srcPixmap,
    const SkColor A8TintColor,
    SkArenaAlloc* allocator)
{
    const SkImageInfo& imageInfo = srcPixmap.info();
    SkISize dimensions = imageInfo.dimensions();

    // Special case samplers with fully expanded templates
    if (imageInfo.gammaCloseToSRGB()) {
        if (filterQuality == kNone_SkFilterQuality) {
            switch (imageInfo.colorType()) {
                case kN32_SkColorType: {
                    using Sampler =
                    NearestNeighborSampler<
                        PixelAccessor<kN32_SkColorType, kSRGB_SkGammaType>, Blender>;
                    return allocator->make<Sampler>(next, srcPixmap);
                }
                case kIndex_8_SkColorType: {
                    using Sampler =
                    NearestNeighborSampler<
                        PixelAccessor<kIndex_8_SkColorType, kSRGB_SkGammaType>, Blender>;
                    return allocator->make<Sampler>(next, srcPixmap);
                }
                default:
                    break;
            }
        } else {
            switch (imageInfo.colorType()) {
                case kN32_SkColorType: {
                    using Sampler =
                    BilerpSampler<
                        PixelAccessor<kN32_SkColorType, kSRGB_SkGammaType>, Blender>;
                    return allocator->make<Sampler>(next, dimensions, xTile, yTile, srcPixmap);
                }
                case kIndex_8_SkColorType: {
                    using Sampler =
                    BilerpSampler<
                        PixelAccessor<kIndex_8_SkColorType, kSRGB_SkGammaType>, Blender>;
                    return allocator->make<Sampler>(next, dimensions, xTile, yTile, srcPixmap);
                }
                default:
                    break;
            }
        }
    }

    auto pixelAccessor = this->choosePixelAccessor(srcPixmap, A8TintColor, allocator);
    // General cases.
    if (filterQuality == kNone_SkFilterQuality) {
        using Sampler = NearestNeighborSampler<PixelAccessorShim, Blender>;
        return allocator->make<Sampler>(next, pixelAccessor);
    } else {
        using Sampler = BilerpSampler<PixelAccessorShim, Blender>;
        return allocator->make<Sampler>(next, dimensions, xTile, yTile, pixelAccessor);
    }
}

Blender* SkLinearBitmapPipeline::chooseBlenderForShading(
    SkAlphaType alphaType,
    float postAlpha,
    SkArenaAlloc* allocator)
{
    if (alphaType == kUnpremul_SkAlphaType) {
        return allocator->make<SrcFPPixel<kUnpremul_SkAlphaType>>(postAlpha);
    } else {
        // kOpaque_SkAlphaType is treated the same as kPremul_SkAlphaType
        return allocator->make<SrcFPPixel<kPremul_SkAlphaType>>(postAlpha);
    }
}