| /* |
| * Copyright 2022 Google LLC |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "include/core/SkCombinationBuilder.h" |
| |
| #include "src/core/SkKeyContext.h" |
| #include "src/core/SkKeyHelpers.h" |
| #include "src/core/SkMathPriv.h" |
| #include "src/core/SkShaderCodeDictionary.h" |
| |
| #ifdef SK_GRAPHITE_ENABLED |
| #include "src/gpu/graphite/ContextPriv.h" |
| #endif |
| |
| #define SHADER_TYPES(M) \ |
| M(SolidColor) \ |
| \ |
| M(LinearGradient) \ |
| M(RadialGradient) \ |
| M(SweepGradient) \ |
| M(ConicalGradient) \ |
| \ |
| M(LocalMatrix) \ |
| M(Image) \ |
| M(BlendShader) \ |
| M(RuntimeShader) |
| |
| // To keep the SHADER_TYPES list and SkShaderType aligned, we create a hidden enum class |
| // and check it against SkShaderType |
| #define MAKE_ENUM(T) k##T, |
| enum class UnusedShaderType { SHADER_TYPES(MAKE_ENUM) }; |
| #undef MAKE_ENUM |
| static constexpr int kUnusedShaderTypeCount = |
| static_cast<int>(UnusedShaderType::kRuntimeShader) + 1; |
| |
| #define STATIC_ASSERT(T) static_assert((int) SkShaderType::k##T == (int) UnusedShaderType::k##T); |
| SHADER_TYPES(STATIC_ASSERT) |
| #undef STATIC_ASSERT |
| |
| static_assert(kSkShaderTypeCount == kUnusedShaderTypeCount); |
| |
| //-------------------------------------------------------------------------------------------------- |
| namespace { |
| |
| int tm_pair_to_index(SkTileModePair tileModes) { |
| static_assert(kSkTileModeCount <= 4); |
| |
| return (int) tileModes.fX << 2 | (int) tileModes.fY; |
| } |
| |
| #ifdef SK_DEBUG |
| SkTileModePair index_to_tm_pair(int index) { |
| static_assert(kSkTileModeCount <= 4); |
| SkASSERT(index < 16); |
| |
| int tmX = index >> 2; |
| int tmY = index & 3; |
| |
| return { (SkTileMode) tmX, (SkTileMode) tmY }; |
| } |
| |
| void add_indent(int indent) { |
| SkDebugf("%*c", indent, ' '); |
| } |
| |
| const char* type_to_str(SkShaderType type) { |
| switch (type) { |
| #define CASE(T) case SkShaderType::k##T: return #T; |
| SHADER_TYPES(CASE) |
| #undef CASE |
| } |
| |
| SkUNREACHABLE; |
| } |
| |
| #endif // SK_DEBUG |
| |
| } // anonymous namespace |
| |
| //-------------------------------------------------------------------------------------------------- |
| |
| // The base class for all the objects stored in the arena |
| // The base option object consists of some number of child slots each of which is an array of |
| // options. The slots and their associated linked list of options are allocated separately in |
| // the arena. |
| class SkOption { |
| public: |
| // The SkSlot holds the combinatorial options for a single child slot of an |
| // option (in a linked list). |
| class SkSlot { |
| public: |
| void addOption(SkOption* newOption); |
| |
| int numCombinations() const; |
| |
| void addToKey(const SkKeyContext&, int desiredCombination, SkPaintParamsKeyBuilder*); |
| |
| #ifdef SK_DEBUG |
| void dump(int indent) const; |
| #endif |
| |
| private: |
| SkOption* fHead = nullptr; |
| SkOption* fTail = nullptr; |
| }; |
| |
| SkOption(SkShaderType type, int numSlots) |
| : fType(type) |
| , fNumSlots(numSlots) {} |
| |
| SkShaderType type() const { return fType; } |
| int numSlots() const { return fNumSlots; } |
| |
| #ifdef SK_DEBUG |
| int epoch() const { return fEpoch; } |
| void setEpoch(int epoch) { |
| SkASSERT(fEpoch == kInvalidEpoch && epoch != kInvalidEpoch); |
| fEpoch = epoch; |
| } |
| #endif |
| |
| void setSlotsArray(SkSlot* slots) { |
| SkASSERT(!fSlots); |
| fSlots = slots; |
| } |
| |
| SkSlot* getSlot(int slotIndex) { |
| if (slotIndex >= fNumSlots) { |
| return nullptr; |
| } |
| |
| return &fSlots[slotIndex]; |
| } |
| |
| void addOption(int slotIndex, SkOption* newOption) { |
| if (slotIndex >= fNumSlots) { |
| return; |
| } |
| |
| SkSlot* slot = this->getSlot(slotIndex); |
| slot->addOption(newOption); |
| } |
| |
| int numChildCombinations() const { |
| int numChildCombinations = 1; |
| for (int i = 0; i < fNumSlots; ++i) { |
| numChildCombinations *= fSlots[i].numCombinations(); |
| } |
| return numChildCombinations; |
| } |
| |
| int numCombinations() const { |
| return this->numIntrinsicCombinations() * this->numChildCombinations(); |
| } |
| |
| void addToKey(const SkKeyContext& keyContext, |
| int desiredCombination, |
| SkPaintParamsKeyBuilder* keyBuilder) { |
| SkASSERT(desiredCombination < this->numCombinations()); |
| |
| int intrinsicCombination = desiredCombination / this->numChildCombinations(); |
| int childCombination = desiredCombination % this->numChildCombinations(); |
| |
| this->beginBlock(keyContext, intrinsicCombination, keyBuilder); |
| |
| if (fNumSlots) { |
| int numCombinationsSeen = 0; |
| for (int slotIndex = 0; slotIndex < fNumSlots; ++slotIndex) { |
| SkSlot* slot = this->getSlot(slotIndex); |
| |
| numCombinationsSeen += slot->numCombinations(); |
| int numCombosLeft = this->numChildCombinations() / numCombinationsSeen; |
| |
| int slotCombination; |
| if (slotIndex+1 < fNumSlots) { |
| slotCombination = childCombination / (numCombosLeft ? numCombosLeft : 1); |
| childCombination %= numCombosLeft; |
| } else { |
| slotCombination = childCombination; |
| } |
| |
| slot->addToKey(keyContext, slotCombination, keyBuilder); |
| } |
| } |
| |
| keyBuilder->endBlock(); |
| } |
| |
| #ifdef SK_DEBUG |
| void dump(int indent = 0) const { |
| SkDebugf("%s", type_to_str(fType)); |
| if (!this->numIntrinsicCombinations()) { |
| SkDebugf("(%d)", this->numIntrinsicCombinations()); |
| } |
| SkDebugf(" { %d, %d }", this->numCombinations(), this->numChildCombinations()); |
| if (this->numSlots()) { |
| SkDebugf("\n"); |
| } |
| int childIndex = 0; |
| for (int i = 0; i < fNumSlots; ++i) { |
| add_indent(indent+4); |
| SkDebugf("%d: ", childIndex); |
| fSlots[i].dump(indent+4); |
| ++childIndex; |
| } |
| } |
| #endif |
| |
| private: |
| int numIntrinsicCombinations() const; |
| void beginBlock(const SkKeyContext&, int intrinsicCombination, SkPaintParamsKeyBuilder*) const; |
| |
| SkDEBUGCODE(static constexpr int kInvalidEpoch = -1;) |
| |
| const SkShaderType fType; |
| const int fNumSlots; |
| SkDEBUGCODE(int fEpoch = kInvalidEpoch;) |
| SkOption* fNext = nullptr; |
| SkSlot* fSlots = nullptr; // an array of 'fNumSlots' SkSlots |
| }; |
| |
| void SkOption::SkSlot::addOption(SkOption* newOption) { |
| SkASSERT(newOption->fNext == nullptr); |
| |
| if (fHead == nullptr) { |
| SkASSERT(fTail == nullptr); |
| fHead = fTail = newOption; |
| } else { |
| SkASSERT(fTail->fNext == nullptr); |
| fTail->fNext = newOption; |
| fTail = newOption; |
| } |
| } |
| |
| int SkOption::SkSlot::numCombinations() const { |
| int numCombinations = 0; |
| |
| for (SkOption* option = fHead; option; option = option->fNext) { |
| numCombinations += option->numCombinations(); |
| } |
| |
| return numCombinations; |
| } |
| |
| void SkOption::SkSlot::addToKey(const SkKeyContext& keyContext, |
| int desiredCombination, |
| SkPaintParamsKeyBuilder* keyBuilder) { |
| SkASSERT(desiredCombination < this->numCombinations()); |
| |
| for (SkOption* option = fHead; option; option = option->fNext) { |
| if (desiredCombination < option->numCombinations()) { |
| option->addToKey(keyContext, desiredCombination, keyBuilder); |
| return; |
| } |
| |
| desiredCombination -= option->numCombinations(); |
| } |
| } |
| |
| #ifdef SK_DEBUG |
| void SkOption::SkSlot::dump(int indent) const { |
| SkDebugf("{ %d } ", this->numCombinations()); |
| |
| for (const SkOption* option = fHead; option; option = option->fNext) { |
| option->dump(indent); |
| SkDebugf(" | "); |
| } |
| SkDebugf("\n"); |
| } |
| #endif |
| |
| #define CREATE_ARENA_OBJECT(T, numChildSlots, ...) \ |
| struct ArenaData_##T : public SkOption { \ |
| static const SkShaderType kType = SkShaderType::k##T; \ |
| static const int kNumChildSlots = numChildSlots; \ |
| int numIntrinsicCombinationsDerived() const; \ |
| void beginBlock(const SkKeyContext&, int intrinsicCombination, SkPaintParamsKeyBuilder*) const;\ |
| __VA_ARGS__ \ |
| }; |
| |
| CREATE_ARENA_OBJECT(SolidColor, /* numChildSlots */ 0) |
| int ArenaData_SolidColor::numIntrinsicCombinationsDerived() const { return 1; } |
| void ArenaData_SolidColor::beginBlock(const SkKeyContext& keyContext, |
| int intrinsicCombination, |
| SkPaintParamsKeyBuilder* builder) const { |
| constexpr SkPMColor4f kUnusedColor = { 1, 0, 0, 1 }; |
| |
| SkASSERT(intrinsicCombination == 0); |
| SolidColorShaderBlock::BeginBlock(keyContext, builder, /*gatherer=*/nullptr, kUnusedColor); |
| } |
| |
| CREATE_ARENA_OBJECT(LinearGradient, /* numChildSlots */ 0, |
| int fMinNumStops; |
| int fMaxNumStops;) |
| int ArenaData_LinearGradient::numIntrinsicCombinationsDerived() const { |
| return fMaxNumStops - fMinNumStops + 1; |
| } |
| void ArenaData_LinearGradient::beginBlock(const SkKeyContext& keyContext, |
| int intrinsicCombination, |
| SkPaintParamsKeyBuilder* builder) const { |
| SkASSERT(intrinsicCombination < this->numIntrinsicCombinationsDerived()); |
| |
| GradientShaderBlocks::BeginBlock(keyContext, builder, /*gatherer=*/nullptr, |
| { SkShader::kLinear_GradientType, |
| fMinNumStops + intrinsicCombination }); |
| } |
| |
| CREATE_ARENA_OBJECT(RadialGradient, /* numChildSlots */ 0, |
| int fMinNumStops; |
| int fMaxNumStops;) |
| int ArenaData_RadialGradient::numIntrinsicCombinationsDerived() const { |
| return fMaxNumStops - fMinNumStops + 1; |
| } |
| void ArenaData_RadialGradient::beginBlock(const SkKeyContext& keyContext, |
| int intrinsicCombination, |
| SkPaintParamsKeyBuilder* builder) const { |
| SkASSERT(intrinsicCombination < this->numIntrinsicCombinationsDerived()); |
| |
| GradientShaderBlocks::BeginBlock(keyContext, builder, /*gatherer=*/nullptr, |
| { SkShader::kRadial_GradientType, |
| fMinNumStops + intrinsicCombination }); |
| } |
| |
| CREATE_ARENA_OBJECT(SweepGradient, /* numChildSlots */ 0, |
| int fMinNumStops; |
| int fMaxNumStops;) |
| int ArenaData_SweepGradient::numIntrinsicCombinationsDerived() const { |
| return fMaxNumStops - fMinNumStops + 1; |
| } |
| void ArenaData_SweepGradient::beginBlock(const SkKeyContext& keyContext, |
| int intrinsicCombination, |
| SkPaintParamsKeyBuilder* builder) const { |
| SkASSERT(intrinsicCombination < this->numIntrinsicCombinationsDerived()); |
| |
| GradientShaderBlocks::BeginBlock(keyContext, builder, /*gatherer=*/nullptr, |
| { SkShader::kSweep_GradientType, |
| fMinNumStops + intrinsicCombination }); |
| } |
| |
| CREATE_ARENA_OBJECT(ConicalGradient, /* numChildSlots */ 0, |
| int fMinNumStops; |
| int fMaxNumStops;) |
| int ArenaData_ConicalGradient::numIntrinsicCombinationsDerived() const { |
| return fMaxNumStops - fMinNumStops + 1; |
| } |
| void ArenaData_ConicalGradient::beginBlock(const SkKeyContext& keyContext, |
| int intrinsicCombination, |
| SkPaintParamsKeyBuilder* builder) const { |
| SkASSERT(intrinsicCombination < this->numIntrinsicCombinationsDerived()); |
| |
| GradientShaderBlocks::BeginBlock(keyContext, builder, /*gatherer=*/nullptr, |
| { SkShader::kConical_GradientType, |
| fMinNumStops + intrinsicCombination }); |
| } |
| |
| CREATE_ARENA_OBJECT(LocalMatrix, /* numChildSlots */ 1) |
| int ArenaData_LocalMatrix::numIntrinsicCombinationsDerived() const { return 1; } |
| void ArenaData_LocalMatrix::beginBlock(const SkKeyContext& keyContext, |
| int intrinsicCombination, |
| SkPaintParamsKeyBuilder* builder) const { |
| SkASSERT(intrinsicCombination == 0); |
| |
| LocalMatrixShaderBlock::BeginBlock(keyContext, builder, /*gatherer=*/nullptr, |
| { SkMatrix::I() }); |
| } |
| |
| // Split out due to constructor work |
| struct ArenaData_Image : public SkOption { |
| static const SkShaderType kType = SkShaderType::kImage; |
| static const int kNumChildSlots = 0; |
| |
| ArenaData_Image(const SkOption& init, SkSpan<SkTileModePair> tileModePairs) |
| : SkOption(init) |
| , fTileModeCombos(0) { |
| for (auto tmPair : tileModePairs) { |
| int index = tm_pair_to_index(tmPair); |
| |
| #ifdef SK_DEBUG |
| SkASSERT(index < 32); |
| SkTileModePair tmp = index_to_tm_pair(index); |
| SkASSERT(tmp == tmPair); |
| #endif |
| |
| fTileModeCombos |= 0x1 << index; |
| } |
| } |
| |
| int numIntrinsicCombinationsDerived() const; |
| void beginBlock(const SkKeyContext&, int intrinsicCombination, SkPaintParamsKeyBuilder*) const; |
| |
| int32_t fTileModeCombos; |
| }; |
| int ArenaData_Image::numIntrinsicCombinationsDerived() const { |
| return SkPopCount(fTileModeCombos); |
| } |
| void ArenaData_Image::beginBlock(const SkKeyContext& keyContext, |
| int intrinsicCombination, |
| SkPaintParamsKeyBuilder* builder) const { |
| SkASSERT(intrinsicCombination < this->numIntrinsicCombinationsDerived()); |
| |
| ImageShaderBlock::BeginBlock(keyContext, builder, /*gatherer=*/nullptr, |
| // none of the ImageData is used |
| { SkSamplingOptions(), |
| SkTileMode::kClamp, SkTileMode::kClamp, |
| SkRect::MakeEmpty(), SkMatrix::I() }); |
| } |
| |
| CREATE_ARENA_OBJECT(BlendShader, /* numChildSlots */ 2) |
| int ArenaData_BlendShader::numIntrinsicCombinationsDerived() const { return 1; } |
| void ArenaData_BlendShader::beginBlock(const SkKeyContext& keyContext, |
| int intrinsicCombination, |
| SkPaintParamsKeyBuilder* builder) const { |
| SkASSERT(intrinsicCombination == 0); |
| |
| BlendShaderBlock::BeginBlock(keyContext, builder, /*gatherer=*/nullptr, |
| { SkBlendMode::kSrc }); // the blendmode is unused |
| } |
| |
| CREATE_ARENA_OBJECT(RuntimeShader, /* numChildSlots */ 0) |
| int ArenaData_RuntimeShader::numIntrinsicCombinationsDerived() const { return 1; } |
| void ArenaData_RuntimeShader::beginBlock(const SkKeyContext& keyContext, |
| int intrinsicCombination, |
| SkPaintParamsKeyBuilder* builder) const { |
| // TODO(skia:13405): replace with RuntimeShader-specific data |
| constexpr SkPMColor4f kUnusedColor = {1, 0, 1, 1}; |
| |
| SkASSERT(intrinsicCombination == 0); |
| SolidColorShaderBlock::BeginBlock(keyContext, builder, /*gatherer=*/nullptr, kUnusedColor); |
| } |
| |
| // Here to access the derived ArenaData objects |
| int SkOption::numIntrinsicCombinations() const { |
| int numIntrinsicCombinations; |
| |
| #define CASE(T) \ |
| case SkShaderType::k##T: \ |
| numIntrinsicCombinations = \ |
| static_cast<const ArenaData_##T*>(this)->numIntrinsicCombinationsDerived(); \ |
| break; |
| |
| switch (this->type()) { |
| SHADER_TYPES(CASE) |
| } |
| |
| #undef CASE |
| |
| SkASSERT(numIntrinsicCombinations >= 1); // There is always, at least, the existential combo |
| return numIntrinsicCombinations; |
| } |
| |
| // Here to access the derived ArenaData objects |
| void SkOption::beginBlock(const SkKeyContext& keyContext, |
| int intrinsicCombination, |
| SkPaintParamsKeyBuilder* builder) const { |
| #define CASE(T) \ |
| case SkShaderType::k##T: \ |
| static_cast<const ArenaData_##T*>(this)->beginBlock(keyContext, \ |
| intrinsicCombination, \ |
| builder); \ |
| break; |
| |
| switch (this->type()) { |
| SHADER_TYPES(CASE) |
| } |
| |
| #undef CASE |
| } |
| |
| //-------------------------------------------------------------------------------------------------- |
| SkCombinationOption SkCombinationOption::addChildOption(int childIndex, SkShaderType type) { |
| if (!this->isValid() || childIndex >= this->numChildSlots()) { |
| return SkCombinationOption(fBuilder, /*dataInArena=*/nullptr); |
| } |
| |
| SkASSERT(fDataInArena->epoch() == fBuilder->fEpoch); |
| |
| SkOption* child = fBuilder->addOptionInternal(type); |
| if (child) { |
| fDataInArena->addOption(childIndex, child); |
| } |
| |
| return SkCombinationOption(fBuilder, child); |
| } |
| |
| SkCombinationOption SkCombinationOption::addChildOption(int childIndex, SkShaderType type, |
| int minNumStops, int maxNumStops) { |
| if (!this->isValid() || childIndex >= this->numChildSlots()) { |
| return SkCombinationOption(fBuilder, /*dataInArena=*/nullptr); |
| } |
| |
| SkASSERT(fDataInArena->epoch() == fBuilder->fEpoch); |
| |
| SkOption* child = fBuilder->addOptionInternal(type, minNumStops, maxNumStops); |
| if (child) { |
| fDataInArena->addOption(childIndex, child); |
| } |
| |
| return SkCombinationOption(fBuilder, child); |
| } |
| |
| SkCombinationOption SkCombinationOption::addChildOption( |
| int childIndex, SkShaderType type, |
| SkSpan<SkTileModePair> tileModes) { |
| if (!this->isValid() || childIndex >= this->numChildSlots()) { |
| return SkCombinationOption(fBuilder, /*dataInArena=*/nullptr); |
| } |
| |
| SkASSERT(fDataInArena->epoch() == fBuilder->fEpoch); |
| |
| SkOption* child = fBuilder->addOptionInternal(type, tileModes); |
| if (child) { |
| fDataInArena->addOption(childIndex, child); |
| } |
| |
| return SkCombinationOption(fBuilder, child); |
| } |
| |
| SkShaderType SkCombinationOption::type() const { return fDataInArena->type(); } |
| int SkCombinationOption::numChildSlots() const { return fDataInArena->numSlots(); } |
| SkDEBUGCODE(int SkCombinationOption::epoch() const { return fDataInArena->epoch(); }) |
| |
| //-------------------------------------------------------------------------------------------------- |
| SkCombinationBuilder::SkCombinationBuilder(SkShaderCodeDictionary* dict) |
| : fDictionary(dict) { |
| fArena = std::make_unique<SkArenaAllocWithReset>(64); |
| this->reset(); |
| } |
| |
| SkCombinationBuilder::~SkCombinationBuilder() = default; |
| |
| |
| template<typename T, typename... Args> |
| SkOption* SkCombinationBuilder::allocInArena(Args&&... args) { |
| SkOption* arenaObject = fArena->make<T>(T{{ T::kType, T::kNumChildSlots }, |
| std::forward<Args>(args)... }); |
| if (!arenaObject) { |
| return nullptr; |
| } |
| |
| SkASSERT(arenaObject->type() == T::kType); |
| SkASSERT(arenaObject->numSlots() == T::kNumChildSlots); |
| |
| SkDEBUGCODE(arenaObject->setEpoch(fEpoch)); |
| |
| if (T::kNumChildSlots) { |
| arenaObject->setSlotsArray(fArena->makeArrayDefault<SkOption::SkSlot>(T::kNumChildSlots)); |
| } |
| |
| return arenaObject; |
| } |
| |
| void SkCombinationBuilder::addOption(SkBlendMode bm) { |
| SkASSERT(fDictionary->isValidID((int) bm)); |
| |
| fBlendModes |= (0x1 << (int) bm); |
| } |
| |
| void SkCombinationBuilder::addOption(SkBlendMode rangeStart, SkBlendMode rangeEnd) { |
| for (int i = (int)rangeStart; i <= (int) rangeEnd; ++i) { |
| this->addOption((SkBlendMode) i); |
| } |
| } |
| |
| void SkCombinationBuilder::addOption(BlendModeGroup group) { |
| switch (group) { |
| case BlendModeGroup::kPorterDuff: |
| this->addOption(SkBlendMode::kClear, SkBlendMode::kScreen); |
| break; |
| case BlendModeGroup::kAdvanced: |
| this->addOption(SkBlendMode::kOverlay, SkBlendMode::kMultiply); |
| break; |
| case BlendModeGroup::kColorAware: |
| this->addOption(SkBlendMode::kHue, SkBlendMode::kLuminosity); |
| break; |
| case BlendModeGroup::kAll: |
| this->addOption(SkBlendMode::kClear, SkBlendMode::kLastMode); |
| break; |
| } |
| } |
| |
| void SkCombinationBuilder::addOption(SkBlenderID id) { |
| SkASSERT(fDictionary->isValidID(id.asUInt())); |
| |
| fBlenders.add(id); |
| } |
| |
| SkOption* SkCombinationBuilder::addOptionInternal(SkShaderType shaderType) { |
| |
| // TODO: Can we use the X macro trick here to collapse this |
| switch (shaderType) { |
| case SkShaderType::kSolidColor: |
| return this->allocInArena<ArenaData_SolidColor>(); |
| case SkShaderType::kLocalMatrix: |
| return this->allocInArena<ArenaData_LocalMatrix>(); |
| case SkShaderType::kBlendShader: |
| return this->allocInArena<ArenaData_BlendShader>(); |
| default: |
| return nullptr; |
| } |
| } |
| |
| SkOption* SkCombinationBuilder::addOptionInternal(SkShaderType shaderType, |
| int minNumStops, int maxNumStops) { |
| |
| // TODO: Can we use the X macro trick here to collapse this |
| switch (shaderType) { |
| case SkShaderType::kLinearGradient: |
| return this->allocInArena<ArenaData_LinearGradient>(minNumStops, maxNumStops); |
| case SkShaderType::kRadialGradient: |
| return this->allocInArena<ArenaData_RadialGradient>(minNumStops, maxNumStops); |
| case SkShaderType::kSweepGradient: |
| return this->allocInArena<ArenaData_SweepGradient>(minNumStops, maxNumStops); |
| case SkShaderType::kConicalGradient: |
| return this->allocInArena<ArenaData_ConicalGradient>(minNumStops, maxNumStops); |
| default: |
| return nullptr; |
| } |
| } |
| |
| SkOption* SkCombinationBuilder::addOptionInternal(SkShaderType shaderType, |
| SkSpan<SkTileModePair> tileModes) { |
| |
| // TODO: Can we use the X macro trick here to collapse this |
| switch (shaderType) { |
| case SkShaderType::kImage: |
| return this->allocInArena<ArenaData_Image>(tileModes); |
| default: |
| return nullptr; |
| } |
| } |
| |
| SkCombinationOption SkCombinationBuilder::addOption(SkShaderType shaderType) { |
| |
| SkOption* newOption = this->addOptionInternal(shaderType); |
| if (newOption) { |
| fShaderOptions.push_back(newOption); |
| } |
| |
| return { this, newOption }; |
| } |
| |
| SkCombinationOption SkCombinationBuilder::addOption(SkShaderType shaderType, |
| int minNumStops, int maxNumStops) { |
| |
| SkOption* newOption = this->addOptionInternal(shaderType, minNumStops, maxNumStops); |
| if (newOption) { |
| fShaderOptions.push_back(newOption); |
| } |
| |
| return { this, newOption }; |
| } |
| |
| SkCombinationOption SkCombinationBuilder::addOption(SkShaderType shaderType, |
| SkSpan<SkTileModePair> tileModes) { |
| |
| SkOption* newOption = this->addOptionInternal(shaderType, tileModes); |
| if (newOption) { |
| fShaderOptions.push_back(newOption); |
| } |
| |
| return { this, newOption }; |
| } |
| |
| void SkCombinationBuilder::reset() { |
| fShaderOptions.reset(); |
| fBlendModes = 0; |
| fBlenders.reset(); |
| fArena->reset(); |
| SkDEBUGCODE(++fEpoch;) |
| } |
| |
| int SkCombinationBuilder::numShaderCombinations() const { |
| int numShaderCombinations = 0; |
| for (SkOption* s : fShaderOptions) { |
| numShaderCombinations += s->numCombinations(); |
| } |
| |
| // If no shader option is specified the builder will add a solid color shader option |
| return numShaderCombinations ? numShaderCombinations : 1; |
| } |
| |
| int SkCombinationBuilder::numBlendModeCombinations() const { |
| int numBlendModeCombinations = SkPopCount(fBlendModes) + fBlenders.count(); |
| |
| // If no blend mode options are specified the builder will add kSrcOver as an option |
| return numBlendModeCombinations ? numBlendModeCombinations : 1; |
| } |
| |
| #ifdef SK_DEBUG |
| void SkCombinationBuilder::dump() const { |
| for (SkOption* s : fShaderOptions) { |
| s->dump(); |
| SkDebugf("\n"); |
| } |
| } |
| #endif |
| |
| void SkCombinationBuilder::createKey(const SkKeyContext& keyContext, |
| int desiredCombination, |
| SkPaintParamsKeyBuilder* keyBuilder) { |
| SkDEBUGCODE(keyBuilder->checkReset();) |
| SkASSERT(desiredCombination < this->numCombinations()); |
| |
| int numBlendModeCombos = this->numBlendModeCombinations(); |
| |
| int desiredShaderCombination = desiredCombination / numBlendModeCombos; |
| int desiredBlendCombination = desiredCombination % numBlendModeCombos; |
| |
| for (SkOption* shaderOption : fShaderOptions) { |
| if (desiredShaderCombination < shaderOption->numCombinations()) { |
| shaderOption->addToKey(keyContext, desiredShaderCombination, keyBuilder); |
| break; |
| } |
| |
| desiredShaderCombination -= shaderOption->numCombinations(); |
| } |
| |
| if (desiredBlendCombination < SkPopCount(fBlendModes)) { |
| int ith_set_bit = SkNthSet(fBlendModes, desiredBlendCombination); |
| |
| SkASSERT(ith_set_bit < kSkBlendModeCount); |
| SkBlendMode bm = (SkBlendMode) ith_set_bit; |
| |
| BlendModeBlock::BeginBlock(keyContext, keyBuilder, /*gatherer=*/nullptr, bm); // bm is used! |
| keyBuilder->endBlock(); |
| } else { |
| // TODO: need to handle fBlenders here |
| } |
| |
| } |
| |
| void SkCombinationBuilder::buildCombinations( |
| SkShaderCodeDictionary* dict, |
| const std::function<void(SkUniquePaintParamsID)>& func) { |
| SkKeyContext keyContext(dict); |
| SkPaintParamsKeyBuilder builder(dict, SkBackend::kGraphite); |
| |
| // Supply a default kSrcOver if no other blend mode option is provided |
| if (fBlendModes == 0 && fBlenders.empty()) { |
| this->addOption(SkBlendMode::kSrcOver); |
| } |
| |
| // Supply a default solid color shader if no other shader option is provided |
| if (fShaderOptions.empty()) { |
| this->addOption(SkShaderType::kSolidColor); |
| } |
| |
| int numCombos = this->numCombinations(); |
| for (int i = 0; i < numCombos; ++i) { |
| this->createKey(keyContext, i, &builder); |
| |
| auto entry = dict->findOrCreate(&builder); |
| |
| func(entry->uniqueID()); |
| } |
| } |