| /* |
| * Copyright 2006 The Android Open Source Project |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "include/core/SkColorFilter.h" |
| #include "include/private/SkColorData.h" |
| #include "src/base/SkArenaAlloc.h" |
| #include "src/core/SkBlendModePriv.h" |
| #include "src/core/SkBlitRow.h" |
| #include "src/core/SkColorFilterBase.h" |
| #include "src/core/SkColorSpacePriv.h" |
| #include "src/core/SkColorSpaceXformSteps.h" |
| #include "src/core/SkRasterPipeline.h" |
| #include "src/core/SkReadBuffer.h" |
| #include "src/core/SkVM.h" |
| #include "src/core/SkValidationUtils.h" |
| #include "src/core/SkWriteBuffer.h" |
| |
| #ifdef SK_GRAPHITE_ENABLED |
| #include "src/gpu/graphite/KeyHelpers.h" |
| #include "src/gpu/graphite/PaintParamsKey.h" |
| #endif |
| |
| template <SkAlphaType kDstAT = kPremul_SkAlphaType> |
| static SkRGBA4f<kDstAT> map_color(const SkColor4f& c, SkColorSpace* src, SkColorSpace* dst) { |
| SkRGBA4f<kDstAT> color = {c.fR, c.fG, c.fB, c.fA}; |
| SkColorSpaceXformSteps(src, kUnpremul_SkAlphaType, |
| dst, kDstAT).apply(color.vec()); |
| return color; |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////////////////////////// |
| |
| class SkModeColorFilter final : public SkColorFilterBase { |
| public: |
| SkModeColorFilter(const SkColor4f& color, SkBlendMode mode); |
| |
| bool onIsAlphaUnchanged() const override; |
| |
| #if SK_SUPPORT_GPU |
| GrFPResult asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP, |
| GrRecordingContext*, |
| const GrColorInfo&, |
| const SkSurfaceProps&) const override; |
| #endif |
| #ifdef SK_GRAPHITE_ENABLED |
| void addToKey(const skgpu::graphite::KeyContext&, |
| skgpu::graphite::PaintParamsKeyBuilder*, |
| skgpu::graphite::PipelineDataGatherer*) const override; |
| #endif |
| |
| private: |
| friend void ::SkRegisterModeColorFilterFlattenable(); |
| SK_FLATTENABLE_HOOKS(SkModeColorFilter) |
| |
| void flatten(SkWriteBuffer&) const override; |
| bool onAsAColorMode(SkColor*, SkBlendMode*) const override; |
| |
| bool onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const override; |
| skvm::Color onProgram(skvm::Builder*, skvm::Color, |
| const SkColorInfo&, skvm::Uniforms*, SkArenaAlloc*) const override; |
| |
| SkColor4f fColor; // always stored in sRGB |
| SkBlendMode fMode; |
| }; |
| |
| SkModeColorFilter::SkModeColorFilter(const SkColor4f& color, |
| SkBlendMode mode) |
| : fColor(color) |
| , fMode(mode) {} |
| |
| bool SkModeColorFilter::onAsAColorMode(SkColor* color, SkBlendMode* mode) const { |
| if (color) { |
| *color = fColor.toSkColor(); |
| } |
| if (mode) { |
| *mode = fMode; |
| } |
| return true; |
| } |
| |
| bool SkModeColorFilter::onIsAlphaUnchanged() const { |
| switch (fMode) { |
| case SkBlendMode::kDst: //!< [Da, Dc] |
| case SkBlendMode::kSrcATop: //!< [Da, Sc * Da + (1 - Sa) * Dc] |
| return true; |
| default: |
| break; |
| } |
| return false; |
| } |
| |
| void SkModeColorFilter::flatten(SkWriteBuffer& buffer) const { |
| buffer.writeColor4f(fColor); |
| buffer.writeUInt((int) fMode); |
| } |
| |
| sk_sp<SkFlattenable> SkModeColorFilter::CreateProc(SkReadBuffer& buffer) { |
| if (buffer.isVersionLT(SkPicturePriv::kBlend4fColorFilter)) { |
| // Color is 8-bit, sRGB |
| SkColor color = buffer.readColor(); |
| SkBlendMode mode = (SkBlendMode)buffer.readUInt(); |
| return SkColorFilters::Blend(SkColor4f::FromColor(color), /*sRGB*/nullptr, mode); |
| } else { |
| // Color is 32-bit, sRGB |
| SkColor4f color; |
| buffer.readColor4f(&color); |
| SkBlendMode mode = (SkBlendMode)buffer.readUInt(); |
| return SkColorFilters::Blend(color, /*sRGB*/nullptr, mode); |
| } |
| } |
| |
| bool SkModeColorFilter::onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const { |
| rec.fPipeline->append(SkRasterPipelineOp::move_src_dst); |
| SkPMColor4f color = map_color(fColor, sk_srgb_singleton(), rec.fDstCS); |
| rec.fPipeline->append_constant_color(rec.fAlloc, color.vec()); |
| SkBlendMode_AppendStages(fMode, rec.fPipeline); |
| return true; |
| } |
| |
| skvm::Color SkModeColorFilter::onProgram(skvm::Builder* p, skvm::Color c, |
| const SkColorInfo& dstInfo, |
| skvm::Uniforms* uniforms, SkArenaAlloc*) const { |
| SkPMColor4f color = map_color(fColor, sk_srgb_singleton(), dstInfo.colorSpace()); |
| // The blend program operates on this as if it were premul but the API takes an SkColor4f |
| skvm::Color dst = c, |
| src = p->uniformColor({color.fR, color.fG, color.fB, color.fA}, uniforms); |
| return p->blend(fMode, src,dst); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| #if SK_SUPPORT_GPU |
| #include "src/gpu/Blend.h" |
| #include "src/gpu/ganesh/GrColorInfo.h" |
| #include "src/gpu/ganesh/GrFragmentProcessor.h" |
| #include "src/gpu/ganesh/SkGr.h" |
| #include "src/gpu/ganesh/effects/GrBlendFragmentProcessor.h" |
| |
| GrFPResult SkModeColorFilter::asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP, |
| GrRecordingContext*, |
| const GrColorInfo& dstColorInfo, |
| const SkSurfaceProps& props) const { |
| if (fMode == SkBlendMode::kDst) { |
| // If the blend mode is "dest," the blend color won't factor into it at all. |
| // We can return the input FP as-is. |
| return GrFPSuccess(std::move(inputFP)); |
| } |
| |
| SkDEBUGCODE(const bool fpHasConstIO = !inputFP || inputFP->hasConstantOutputForConstantInput();) |
| |
| SkPMColor4f color = map_color(fColor, sk_srgb_singleton(), dstColorInfo.colorSpace()); |
| |
| auto colorFP = GrFragmentProcessor::MakeColor(color); |
| auto xferFP = GrBlendFragmentProcessor::Make(std::move(colorFP), std::move(inputFP), fMode); |
| |
| if (xferFP == nullptr) { |
| // This is only expected to happen if the blend mode is "dest" and the input FP is null. |
| // Since we already did an early-out in the "dest" blend mode case, we shouldn't get here. |
| SkDEBUGFAIL("GrBlendFragmentProcessor::Make returned null unexpectedly"); |
| return GrFPFailure(nullptr); |
| } |
| |
| // With a solid color input this should always be able to compute the blended color |
| // (at least for coeff modes). |
| // Occasionally, we even do better than we started; specifically, in "src" blend mode, we end up |
| // ditching the input FP entirely, which turns a non-constant operation into a constant one. |
| SkASSERT(fMode > SkBlendMode::kLastCoeffMode || |
| xferFP->hasConstantOutputForConstantInput() >= fpHasConstIO); |
| |
| return GrFPSuccess(std::move(xferFP)); |
| } |
| |
| #endif |
| |
| #ifdef SK_GRAPHITE_ENABLED |
| void SkModeColorFilter::addToKey(const skgpu::graphite::KeyContext& keyContext, |
| skgpu::graphite::PaintParamsKeyBuilder* builder, |
| skgpu::graphite::PipelineDataGatherer* gatherer) const { |
| using namespace skgpu::graphite; |
| |
| // TODO: Take into account the render target color space once graphite has color management. |
| SkPMColor4f color = map_color(fColor, sk_srgb_singleton(), nullptr); |
| BlendColorFilterBlock::BlendColorFilterData data(fMode, color); |
| |
| BlendColorFilterBlock::BeginBlock(keyContext, builder, gatherer, &data); |
| builder->endBlock(); |
| } |
| |
| #endif |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| sk_sp<SkColorFilter> SkColorFilters::Blend(const SkColor4f& color, |
| sk_sp<SkColorSpace> colorSpace, |
| SkBlendMode mode) { |
| if (!SkIsValidMode(mode)) { |
| return nullptr; |
| } |
| |
| // First map to sRGB to simplify storage in the actual SkColorFilter instance, staying unpremul |
| // until the final dst color space is known when actually filtering. |
| SkColor4f srgb = map_color<kUnpremul_SkAlphaType>( |
| color, colorSpace.get(), sk_srgb_singleton()); |
| |
| // Next collapse some modes if possible |
| float alpha = srgb.fA; |
| if (SkBlendMode::kClear == mode) { |
| srgb = SkColors::kTransparent; |
| mode = SkBlendMode::kSrc; |
| } else if (SkBlendMode::kSrcOver == mode) { |
| if (0.f == alpha) { |
| mode = SkBlendMode::kDst; |
| } else if (1.f == alpha) { |
| mode = SkBlendMode::kSrc; |
| } |
| // else just stay srcover |
| } |
| |
| // Finally weed out combinations that are noops, and just return null |
| if (SkBlendMode::kDst == mode || |
| (0.f == alpha && (SkBlendMode::kSrcOver == mode || |
| SkBlendMode::kDstOver == mode || |
| SkBlendMode::kDstOut == mode || |
| SkBlendMode::kSrcATop == mode || |
| SkBlendMode::kXor == mode || |
| SkBlendMode::kDarken == mode)) || |
| (1.f == alpha && SkBlendMode::kDstIn == mode)) { |
| return nullptr; |
| } |
| |
| return sk_sp<SkColorFilter>(new SkModeColorFilter(srgb, mode)); |
| } |
| |
| sk_sp<SkColorFilter> SkColorFilters::Blend(SkColor color, SkBlendMode mode) { |
| return Blend(SkColor4f::FromColor(color), /*sRGB*/nullptr, mode); |
| } |
| |
| void SkRegisterModeColorFilterFlattenable() { |
| SK_REGISTER_FLATTENABLE(SkModeColorFilter); |
| } |