/* * 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 "SkArenaAlloc.h" #include "SkColorFilter.h" #include "SkColorSpaceXformer.h" #include "SkNx.h" #include "SkPM4f.h" #include "SkRasterPipeline.h" #include "SkReadBuffer.h" #include "SkRefCnt.h" #include "SkString.h" #include "SkTDArray.h" #include "SkUnPreMultiply.h" #include "SkWriteBuffer.h" #include "../jumper/SkJumper.h" #if SK_SUPPORT_GPU #include "GrFragmentProcessor.h" #endif bool SkColorFilter::asColorMode(SkColor*, SkBlendMode*) const { return false; } bool SkColorFilter::asColorMatrix(SkScalar matrix[20]) const { return false; } bool SkColorFilter::asComponentTable(SkBitmap*) const { return false; } #if SK_SUPPORT_GPU std::unique_ptr SkColorFilter::asFragmentProcessor( GrContext*, const GrColorSpaceInfo&) const { return nullptr; } #endif void SkColorFilter::appendStages(SkRasterPipeline* p, SkColorSpace* dstCS, SkArenaAlloc* alloc, bool shaderIsOpaque) const { this->onAppendStages(p, dstCS, alloc, shaderIsOpaque); } SkColor SkColorFilter::filterColor(SkColor c) const { const float inv255 = 1.0f / 255; SkColor4f c4 = this->filterColor4f({ SkColorGetR(c) * inv255, SkColorGetG(c) * inv255, SkColorGetB(c) * inv255, SkColorGetA(c) * inv255, }); return SkColorSetARGB(sk_float_round2int(c4.fA*255), sk_float_round2int(c4.fR*255), sk_float_round2int(c4.fG*255), sk_float_round2int(c4.fB*255)); } #include "SkRasterPipeline.h" SkColor4f SkColorFilter::filterColor4f(const SkColor4f& c) const { SkPM4f dst, src = c.premul(); SkSTArenaAlloc<128> alloc; SkRasterPipeline pipeline(&alloc); pipeline.append_constant_color(&alloc, src); this->onAppendStages(&pipeline, nullptr, &alloc, c.fA == 1); SkJumper_MemoryCtx dstPtr = { &dst, 0 }; pipeline.append(SkRasterPipeline::store_f32, &dstPtr); pipeline.run(0,0, 1,1); return dst.unpremul(); } /////////////////////////////////////////////////////////////////////////////////////////////////// /* * Since colorfilters may be used on the GPU backend, and in that case we may string together * many GrFragmentProcessors, we might exceed some internal instruction/resource limit. * * Since we don't yet know *what* those limits might be when we construct the final shader, * we just set an arbitrary limit during construction. If later we find smarter ways to know what * the limnits are, we can change this constant (or remove it). */ #define SK_MAX_COMPOSE_COLORFILTER_COUNT 4 class SkComposeColorFilter : public SkColorFilter { public: uint32_t getFlags() const override { // Can only claim alphaunchanged and SkPM4f support if both our proxys do. return fOuter->getFlags() & fInner->getFlags(); } void onAppendStages(SkRasterPipeline* p, SkColorSpace* dst, SkArenaAlloc* scratch, bool shaderIsOpaque) const override { bool innerIsOpaque = shaderIsOpaque; if (!(fInner->getFlags() & kAlphaUnchanged_Flag)) { innerIsOpaque = false; } fInner->appendStages(p, dst, scratch, shaderIsOpaque); fOuter->appendStages(p, dst, scratch, innerIsOpaque); } #if SK_SUPPORT_GPU std::unique_ptr asFragmentProcessor( GrContext* context, const GrColorSpaceInfo& dstColorSpaceInfo) const override { auto innerFP = fInner->asFragmentProcessor(context, dstColorSpaceInfo); auto outerFP = fOuter->asFragmentProcessor(context, dstColorSpaceInfo); if (!innerFP || !outerFP) { return nullptr; } std::unique_ptr series[] = { std::move(innerFP), std::move(outerFP) }; return GrFragmentProcessor::RunInSeries(series, 2); } #endif SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkComposeColorFilter) protected: void flatten(SkWriteBuffer& buffer) const override { buffer.writeFlattenable(fOuter.get()); buffer.writeFlattenable(fInner.get()); } private: SkComposeColorFilter(sk_sp outer, sk_sp inner, int composedFilterCount) : fOuter(std::move(outer)) , fInner(std::move(inner)) , fComposedFilterCount(composedFilterCount) { SkASSERT(composedFilterCount >= 2); SkASSERT(composedFilterCount <= SK_MAX_COMPOSE_COLORFILTER_COUNT); } int privateComposedFilterCount() const override { return fComposedFilterCount; } sk_sp onMakeColorSpace(SkColorSpaceXformer* xformer) const override { auto outer = xformer->apply(fOuter.get()); auto inner = xformer->apply(fInner.get()); if (outer != fOuter || inner != fInner) { return outer->makeComposed(inner); } return this->INHERITED::onMakeColorSpace(xformer); } sk_sp fOuter; sk_sp fInner; const int fComposedFilterCount; friend class SkColorFilter; typedef SkColorFilter INHERITED; }; sk_sp SkComposeColorFilter::CreateProc(SkReadBuffer& buffer) { sk_sp outer(buffer.readColorFilter()); sk_sp inner(buffer.readColorFilter()); return outer ? outer->makeComposed(std::move(inner)) : inner; } sk_sp SkColorFilter::makeComposed(sk_sp inner) const { if (!inner) { return sk_ref_sp(this); } // Give the subclass a shot at a more optimal composition... auto composition = this->onMakeComposed(inner); if (composition) { return composition; } int count = inner->privateComposedFilterCount() + this->privateComposedFilterCount(); if (count > SK_MAX_COMPOSE_COLORFILTER_COUNT) { return nullptr; } return sk_sp(new SkComposeColorFilter(sk_ref_sp(this), std::move(inner), count)); } /////////////////////////////////////////////////////////////////////////////////////////////////// #if SK_SUPPORT_GPU #include "../gpu/effects/GrSRGBEffect.h" #endif class SkSRGBGammaColorFilter : public SkColorFilter { public: enum class Direction { kLinearToSRGB, kSRGBToLinear, }; SkSRGBGammaColorFilter(Direction dir) : fDir(dir) {} #if SK_SUPPORT_GPU std::unique_ptr asFragmentProcessor( GrContext*, const GrColorSpaceInfo&) const override { // wish our caller would let us know if our input was opaque... GrSRGBEffect::Alpha alpha = GrSRGBEffect::Alpha::kPremul; switch (fDir) { case Direction::kLinearToSRGB: return GrSRGBEffect::Make(GrSRGBEffect::Mode::kLinearToSRGB, alpha); case Direction::kSRGBToLinear: return GrSRGBEffect::Make(GrSRGBEffect::Mode::kSRGBToLinear, alpha); } return nullptr; } #endif SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSRGBGammaColorFilter) void onAppendStages(SkRasterPipeline* p, SkColorSpace*, SkArenaAlloc* alloc, bool shaderIsOpaque) const override { if (!shaderIsOpaque) { p->append(SkRasterPipeline::unpremul); } switch (fDir) { case Direction::kLinearToSRGB: p->append(SkRasterPipeline::to_srgb); break; case Direction::kSRGBToLinear: p->append(SkRasterPipeline::from_srgb); break; } if (!shaderIsOpaque) { p->append(SkRasterPipeline::premul); } } protected: void flatten(SkWriteBuffer& buffer) const override { buffer.write32(static_cast(fDir)); } private: const Direction fDir; friend class SkColorFilter; typedef SkColorFilter INHERITED; }; sk_sp SkSRGBGammaColorFilter::CreateProc(SkReadBuffer& buffer) { uint32_t dir = buffer.read32(); if (!buffer.validate(dir <= 1)) { return nullptr; } return sk_sp(new SkSRGBGammaColorFilter(static_cast(dir))); } template sk_sp MakeSRGBGammaCF() { static SkColorFilter* gSingleton = new SkSRGBGammaColorFilter(dir); return sk_ref_sp(gSingleton); } sk_sp SkColorFilter::MakeLinearToSRGBGamma() { return MakeSRGBGammaCF(); } sk_sp SkColorFilter::MakeSRGBToLinearGamma() { return MakeSRGBGammaCF(); } /////////////////////////////////////////////////////////////////////////////////////////////////// #include "SkModeColorFilter.h" SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkColorFilter) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkComposeColorFilter) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkModeColorFilter) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSRGBGammaColorFilter) SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END