| /* |
| * 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 "SkLinearBitmapPipeline_core.h" |
| #include "SkLinearBitmapPipeline_matrix.h" |
| #include "SkLinearBitmapPipeline_tile.h" |
| #include "SkLinearBitmapPipeline_sample.h" |
| #include "SkNx.h" |
| #include "SkOpts.h" |
| #include "SkPM4f.h" |
| |
| //////////////////////////////////////////////////////////////////////////////////////////////////// |
| // SkLinearBitmapPipeline::Stage |
| template<typename Base, size_t kSize, typename Next> |
| SkLinearBitmapPipeline::Stage<Base, kSize, Next>::~Stage() { |
| if (fIsInitialized) { |
| this->get()->~Base(); |
| } |
| } |
| |
| template<typename Base, size_t kSize, typename Next> |
| template<typename Variant, typename... Args> |
| void SkLinearBitmapPipeline::Stage<Base, kSize, Next>::initStage(Next* next, Args&& ... args) { |
| SkASSERTF(sizeof(Variant) <= sizeof(fSpace), |
| "Size Variant: %d, Space: %d", sizeof(Variant), sizeof(fSpace)); |
| |
| new (&fSpace) Variant(next, std::forward<Args>(args)...); |
| fStageCloner = [this](Next* nextClone, void* addr) { |
| new (addr) Variant(nextClone, (const Variant&)*this->get()); |
| }; |
| fIsInitialized = true; |
| }; |
| |
| template<typename Base, size_t kSize, typename Next> |
| template<typename Variant, typename... Args> |
| void SkLinearBitmapPipeline::Stage<Base, kSize, Next>::initSink(Args&& ... args) { |
| SkASSERTF(sizeof(Variant) <= sizeof(fSpace), |
| "Size Variant: %d, Space: %d", sizeof(Variant), sizeof(fSpace)); |
| new (&fSpace) Variant(std::forward<Args>(args)...); |
| fIsInitialized = true; |
| }; |
| |
| template<typename Base, size_t kSize, typename Next> |
| template <typename To, typename From> |
| To* SkLinearBitmapPipeline::Stage<Base, kSize, Next>::getInterface() { |
| From* down = static_cast<From*>(this->get()); |
| return static_cast<To*>(down); |
| } |
| |
| template<typename Base, size_t kSize, typename Next> |
| Base* SkLinearBitmapPipeline::Stage<Base, kSize, Next>::cloneStageTo( |
| Next* next, Stage* cloneToStage) const |
| { |
| if (!fIsInitialized) return nullptr; |
| fStageCloner(next, &cloneToStage->fSpace); |
| return cloneToStage->get(); |
| } |
| |
| 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, const 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>; |
| |
| |
| static SkLinearBitmapPipeline::PointProcessorInterface* choose_matrix( |
| SkLinearBitmapPipeline::PointProcessorInterface* next, |
| const SkMatrix& inverse, |
| SkLinearBitmapPipeline::MatrixStage* matrixProc) { |
| if (inverse.hasPerspective()) { |
| matrixProc->initStage<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)); |
| } else if (inverse.getSkewX() != 0.0f || inverse.getSkewY() != 0.0f) { |
| matrixProc->initStage<AffineMatrix<>>( |
| next, |
| SkVector{inverse.getTranslateX(), inverse.getTranslateY()}, |
| SkVector{inverse.getScaleX(), inverse.getScaleY()}, |
| SkVector{inverse.getSkewX(), inverse.getSkewY()}); |
| } else if (inverse.getScaleX() != 1.0f || inverse.getScaleY() != 1.0f) { |
| matrixProc->initStage<ScaleMatrix<>>( |
| next, |
| SkVector{inverse.getTranslateX(), inverse.getTranslateY()}, |
| SkVector{inverse.getScaleX(), inverse.getScaleY()}); |
| } else if (inverse.getTranslateX() != 0.0f || inverse.getTranslateY() != 0.0f) { |
| matrixProc->initStage<TranslateMatrix<>>( |
| next, |
| SkVector{inverse.getTranslateX(), inverse.getTranslateY()}); |
| } else { |
| return next; |
| } |
| return matrixProc->get(); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////////////////////////// |
| // 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, const 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; |
| }; |
| |
| template <typename XStrategy, typename Next> |
| void choose_tiler_ymode( |
| SkShader::TileMode yMode, SkFilterQuality filterQuality, SkISize dimensions, |
| Next* next, |
| SkLinearBitmapPipeline::TileStage* tileStage) { |
| switch (yMode) { |
| case SkShader::kClamp_TileMode: { |
| using Tiler = CombinedTileStage<XStrategy, YClampStrategy, Next>; |
| tileStage->initStage<Tiler>(next, dimensions); |
| break; |
| } |
| case SkShader::kRepeat_TileMode: { |
| using Tiler = CombinedTileStage<XStrategy, YRepeatStrategy, Next>; |
| tileStage->initStage<Tiler>(next, dimensions); |
| break; |
| } |
| case SkShader::kMirror_TileMode: { |
| using Tiler = CombinedTileStage<XStrategy, YMirrorStrategy, Next>; |
| tileStage->initStage<Tiler>(next, dimensions); |
| break; |
| } |
| } |
| }; |
| |
| static SkLinearBitmapPipeline::PointProcessorInterface* choose_tiler( |
| SkLinearBitmapPipeline::SampleProcessorInterface* next, |
| SkISize dimensions, |
| SkShader::TileMode xMode, |
| SkShader::TileMode yMode, |
| SkFilterQuality filterQuality, |
| SkScalar dx, |
| SkLinearBitmapPipeline::TileStage* tileStage) |
| { |
| switch (xMode) { |
| case SkShader::kClamp_TileMode: |
| choose_tiler_ymode<XClampStrategy>(yMode, filterQuality, dimensions, next, tileStage); |
| break; |
| case SkShader::kRepeat_TileMode: |
| if (dx == 1.0f && filterQuality == kNone_SkFilterQuality) { |
| choose_tiler_ymode<XRepeatUnitScaleStrategy>( |
| yMode, kNone_SkFilterQuality, dimensions, next, tileStage); |
| } else { |
| choose_tiler_ymode<XRepeatStrategy>( |
| yMode, filterQuality, dimensions, next, tileStage); |
| } |
| break; |
| case SkShader::kMirror_TileMode: |
| choose_tiler_ymode<XMirrorStrategy>(yMode, filterQuality, dimensions, next, tileStage); |
| break; |
| } |
| |
| return tileStage->get(); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////////////////////////// |
| // 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; |
| |
| template <SkColorType colorType> |
| static SkLinearBitmapPipeline::PixelAccessorInterface* choose_specific_accessor( |
| const SkPixmap& srcPixmap, SkLinearBitmapPipeline::Accessor* accessor) |
| { |
| if (srcPixmap.info().gammaCloseToSRGB()) { |
| using PA = PixelAccessor<colorType, kSRGB_SkGammaType>; |
| accessor->init<PA>(srcPixmap); |
| return accessor->get(); |
| } else { |
| using PA = PixelAccessor<colorType, kLinear_SkGammaType>; |
| accessor->init<PA>(srcPixmap); |
| return accessor->get(); |
| } |
| } |
| |
| static SkLinearBitmapPipeline::PixelAccessorInterface* choose_pixel_accessor( |
| const SkPixmap& srcPixmap, |
| const SkColor A8TintColor, |
| SkLinearBitmapPipeline::Accessor* accessor) |
| { |
| const SkImageInfo& imageInfo = srcPixmap.info(); |
| |
| SkLinearBitmapPipeline::PixelAccessorInterface* pixelAccessor = nullptr; |
| switch (imageInfo.colorType()) { |
| case kAlpha_8_SkColorType: { |
| using PA = PixelAccessor<kAlpha_8_SkColorType, kLinear_SkGammaType>; |
| accessor->init<PA>(srcPixmap, A8TintColor); |
| pixelAccessor = accessor->get(); |
| } |
| break; |
| case kARGB_4444_SkColorType: |
| pixelAccessor = choose_specific_accessor<kARGB_4444_SkColorType>(srcPixmap, accessor); |
| break; |
| case kRGB_565_SkColorType: |
| pixelAccessor = choose_specific_accessor<kRGB_565_SkColorType>(srcPixmap, accessor); |
| break; |
| case kRGBA_8888_SkColorType: |
| pixelAccessor = choose_specific_accessor<kRGBA_8888_SkColorType>(srcPixmap, accessor); |
| break; |
| case kBGRA_8888_SkColorType: |
| pixelAccessor = choose_specific_accessor<kBGRA_8888_SkColorType>(srcPixmap, accessor); |
| break; |
| case kIndex_8_SkColorType: |
| pixelAccessor = choose_specific_accessor<kIndex_8_SkColorType>(srcPixmap, accessor); |
| break; |
| case kGray_8_SkColorType: |
| pixelAccessor = choose_specific_accessor<kGray_8_SkColorType>(srcPixmap, accessor); |
| break; |
| case kRGBA_F16_SkColorType: { |
| using PA = PixelAccessor<kRGBA_F16_SkColorType, kLinear_SkGammaType>; |
| accessor->init<PA>(srcPixmap); |
| pixelAccessor = accessor->get(); |
| } |
| break; |
| default: |
| SkFAIL("Not implemented. Unsupported src"); |
| break; |
| } |
| |
| return pixelAccessor; |
| } |
| |
| SkLinearBitmapPipeline::SampleProcessorInterface* choose_pixel_sampler( |
| Blender* next, |
| SkFilterQuality filterQuality, |
| SkShader::TileMode xTile, SkShader::TileMode yTile, |
| const SkPixmap& srcPixmap, |
| const SkColor A8TintColor, |
| SkLinearBitmapPipeline::SampleStage* sampleStage, |
| SkLinearBitmapPipeline::Accessor* accessor) { |
| 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 S = |
| NearestNeighborSampler< |
| PixelAccessor<kN32_SkColorType, kSRGB_SkGammaType>, Blender>; |
| sampleStage->initStage<S>(next, srcPixmap); |
| return sampleStage->get(); |
| } |
| case kIndex_8_SkColorType: { |
| using S = |
| NearestNeighborSampler< |
| PixelAccessor<kIndex_8_SkColorType, kSRGB_SkGammaType>, Blender>; |
| sampleStage->initStage<S>(next, srcPixmap); |
| return sampleStage->get(); |
| } |
| default: |
| break; |
| } |
| } else { |
| switch (imageInfo.colorType()) { |
| case kN32_SkColorType: { |
| using S = |
| BilerpSampler< |
| PixelAccessor<kN32_SkColorType, kSRGB_SkGammaType>, Blender>; |
| sampleStage->initStage<S>(next, dimensions, xTile, yTile, srcPixmap); |
| return sampleStage->get(); |
| } |
| case kIndex_8_SkColorType: { |
| using S = |
| BilerpSampler< |
| PixelAccessor<kIndex_8_SkColorType, kSRGB_SkGammaType>, Blender>; |
| sampleStage->initStage<S>(next, dimensions, xTile, yTile, srcPixmap); |
| return sampleStage->get(); |
| } |
| default: |
| break; |
| } |
| } |
| } |
| |
| auto pixelAccessor = choose_pixel_accessor(srcPixmap, A8TintColor, accessor); |
| // General cases. |
| if (filterQuality == kNone_SkFilterQuality) { |
| using S = NearestNeighborSampler<PixelAccessorShim, Blender>; |
| sampleStage->initStage<S>(next, pixelAccessor); |
| } else { |
| using S = BilerpSampler<PixelAccessorShim, Blender>; |
| sampleStage->initStage<S>(next, dimensions, xTile, yTile, pixelAccessor); |
| } |
| return sampleStage->get(); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////////////////////////// |
| // Pixel Blender Stage |
| template <SkAlphaType alphaType> |
| class SrcFPPixel final : public SkLinearBitmapPipeline::BlendProcessorInterface { |
| 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; |
| Sk4f fPostAlpha; |
| }; |
| |
| static SkLinearBitmapPipeline::BlendProcessorInterface* choose_blender_for_shading( |
| SkAlphaType alphaType, |
| float postAlpha, |
| SkLinearBitmapPipeline::BlenderStage* blenderStage) { |
| if (alphaType == kUnpremul_SkAlphaType) { |
| blenderStage->initSink<SrcFPPixel<kUnpremul_SkAlphaType>>(postAlpha); |
| } else { |
| // kOpaque_SkAlphaType is treated the same as kPremul_SkAlphaType |
| blenderStage->initSink<SrcFPPixel<kPremul_SkAlphaType>>(postAlpha); |
| } |
| return blenderStage->get(); |
| } |
| |
| } // namespace |
| |
| //////////////////////////////////////////////////////////////////////////////////////////////////// |
| // SkLinearBitmapPipeline |
| SkLinearBitmapPipeline::~SkLinearBitmapPipeline() {} |
| |
| SkLinearBitmapPipeline::SkLinearBitmapPipeline( |
| const SkMatrix& inverse, |
| SkFilterQuality filterQuality, |
| SkShader::TileMode xTile, SkShader::TileMode yTile, |
| SkColor paintColor, |
| const SkPixmap& srcPixmap) |
| { |
| 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 = choose_blender_for_shading(alphaType, postAlpha, &fBlenderStage); |
| auto samplerStage = choose_pixel_sampler( |
| blenderStage, filterQuality, xTile, yTile, |
| srcPixmap, paintColor, &fSampleStage, &fAccessor); |
| auto tilerStage = choose_tiler(samplerStage, dimensions, xTile, yTile, |
| filterQuality, dx, &fTileStage); |
| fFirstStage = choose_matrix(tilerStage, adjustedInverse, &fMatrixStage); |
| fLastStage = blenderStage; |
| } |
| |
| bool SkLinearBitmapPipeline::ClonePipelineForBlitting( |
| SkEmbeddableLinearPipeline* pipelineStorage, |
| const SkLinearBitmapPipeline& pipeline, |
| SkMatrix::TypeMask matrixMask, |
| SkShader::TileMode xTileMode, |
| SkShader::TileMode yTileMode, |
| SkFilterQuality filterQuality, |
| const SkPixmap& srcPixmap, |
| float finalAlpha, |
| SkXfermode::Mode xferMode, |
| const SkImageInfo& dstInfo) |
| { |
| if (xferMode == SkXfermode::kSrcOver_Mode |
| && srcPixmap.info().alphaType() == kOpaque_SkAlphaType) { |
| xferMode = SkXfermode::kSrc_Mode; |
| } |
| |
| if (matrixMask & ~SkMatrix::kTranslate_Mask ) { return false; } |
| if (filterQuality != SkFilterQuality::kNone_SkFilterQuality) { return false; } |
| if (finalAlpha != 1.0f) { return false; } |
| if (srcPixmap.info().colorType() != kRGBA_8888_SkColorType |
| || dstInfo.colorType() != kRGBA_8888_SkColorType) { return false; } |
| |
| if (!srcPixmap.info().gammaCloseToSRGB() || !dstInfo.gammaCloseToSRGB()) { |
| return false; |
| } |
| |
| if (xferMode != SkXfermode::kSrc_Mode && xferMode != SkXfermode::kSrcOver_Mode) { |
| return false; |
| } |
| |
| pipelineStorage->init(pipeline, srcPixmap, xferMode, dstInfo); |
| |
| return true; |
| } |
| |
| SkLinearBitmapPipeline::SkLinearBitmapPipeline( |
| const SkLinearBitmapPipeline& pipeline, |
| const SkPixmap& srcPixmap, |
| SkXfermode::Mode mode, |
| const SkImageInfo& dstInfo) |
| { |
| SkASSERT(mode == SkXfermode::kSrc_Mode || mode == SkXfermode::kSrcOver_Mode); |
| SkASSERT(srcPixmap.info().colorType() == dstInfo.colorType() |
| && srcPixmap.info().colorType() == kRGBA_8888_SkColorType); |
| |
| if (mode == SkXfermode::kSrc_Mode) { |
| fSampleStage.initSink<RGBA8888UnitRepeatSrc>( |
| srcPixmap.writable_addr32(0, 0), srcPixmap.rowBytes() / 4); |
| fLastStage = fSampleStage.getInterface<DestinationInterface, RGBA8888UnitRepeatSrc>(); |
| } else { |
| fSampleStage.initSink<RGBA8888UnitRepeatSrcOver>( |
| srcPixmap.writable_addr32(0, 0), srcPixmap.rowBytes() / 4); |
| fLastStage = fSampleStage.getInterface<DestinationInterface, RGBA8888UnitRepeatSrcOver>(); |
| } |
| |
| auto sampleStage = fSampleStage.get(); |
| auto tilerStage = pipeline.fTileStage.cloneStageTo(sampleStage, &fTileStage); |
| tilerStage = (tilerStage != nullptr) ? tilerStage : sampleStage; |
| auto matrixStage = pipeline.fMatrixStage.cloneStageTo(tilerStage, &fMatrixStage); |
| matrixStage = (matrixStage != nullptr) ? matrixStage : tilerStage; |
| fFirstStage = matrixStage; |
| } |
| |
| 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}); |
| } |