1/*
2 * Copyright 2006 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkArenaAlloc.h"
9#include "SkBlendModePriv.h"
10#include "SkComposeShader.h"
11#include "SkColorFilter.h"
12#include "SkColorData.h"
13#include "SkColorShader.h"
14#include "SkRasterPipeline.h"
15#include "SkReadBuffer.h"
16#include "SkWriteBuffer.h"
17#include "SkString.h"
18#include "../jumper/SkJumper.h"
19
20sk_sp<SkShader> SkShader::MakeCompose(sk_sp<SkShader> dst, sk_sp<SkShader> src, SkBlendMode mode,
21                                      float lerpT) {
22    if (!src || !dst || SkScalarIsNaN(lerpT)) {
23        return nullptr;
24    }
25    lerpT = SkScalarPin(lerpT, 0, 1);
26
27    if (lerpT == 0) {
28        return dst;
29    } else if (lerpT == 1) {
30        if (mode == SkBlendMode::kSrc) {
31            return src;
32        }
33        if (mode == SkBlendMode::kDst) {
34            return dst;
35        }
36    }
37    return sk_sp<SkShader>(new SkComposeShader(std::move(dst), std::move(src), mode, lerpT));
38}
39
40///////////////////////////////////////////////////////////////////////////////
41
42sk_sp<SkFlattenable> SkComposeShader::CreateProc(SkReadBuffer& buffer) {
43    sk_sp<SkShader> dst(buffer.readShader());
44    sk_sp<SkShader> src(buffer.readShader());
45    unsigned        mode = buffer.read32();
46    float           lerp = buffer.readScalar();
47
48    // check for valid mode before we cast to the enum type
49    if (!buffer.validate(mode <= (unsigned)SkBlendMode::kLastMode)) {
50        return nullptr;
51    }
52    return MakeCompose(std::move(dst), std::move(src), static_cast<SkBlendMode>(mode), lerp);
53}
54
55void SkComposeShader::flatten(SkWriteBuffer& buffer) const {
56    buffer.writeFlattenable(fDst.get());
57    buffer.writeFlattenable(fSrc.get());
58    buffer.write32((int)fMode);
59    buffer.writeScalar(fLerpT);
60}
61
62sk_sp<SkShader> SkComposeShader::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
63    return MakeCompose(xformer->apply(fDst.get()), xformer->apply(fSrc.get()),
64                       fMode, fLerpT);
65}
66
67#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
68bool SkComposeShader::asACompose(ComposeRec* rec) const {
69    if (!this->isJustMode()) {
70        return false;
71    }
72
73    if (rec) {
74        rec->fShaderA   = fDst.get();
75        rec->fShaderB   = fSrc.get();
76        rec->fBlendMode = fMode;
77    }
78    return true;
79}
80#endif
81
82bool SkComposeShader::onAppendStages(const StageRec& rec) const {
83    struct Storage {
84        float   fRGBA[4 * SkJumper_kMaxStride];
85        float   fAlpha;
86    };
87    auto storage = rec.fAlloc->make<Storage>();
88
89    if (!as_SB(fSrc)->appendStages(rec)) {
90        return false;
91    }
92    // This outputs r,g,b,a, which we'll need later when we apply the mode, but we save it off now
93    // since fShaderB will overwrite them.
94    rec.fPipeline->append(SkRasterPipeline::store_rgba, storage->fRGBA);
95
96    if (!as_SB(fDst)->appendStages(rec)) {
97        return false;
98    }
99    // We now have our logical 'dst' in r,g,b,a, but we need it in dr,dg,db,da for the mode/lerp
100    // so we have to shuttle them. If we had a stage the would load_into_dst, then we could
101    // reverse the two shader invocations, and avoid this move...
102    rec.fPipeline->append(SkRasterPipeline::move_src_dst);
103    rec.fPipeline->append(SkRasterPipeline::load_rgba, storage->fRGBA);
104
105    if (!this->isJustLerp()) {
106        SkBlendMode_AppendStages(fMode, rec.fPipeline);
107    }
108    if (!this->isJustMode()) {
109        rec.fPipeline->append(SkRasterPipeline::lerp_1_float, &fLerpT);
110    }
111    return true;
112}
113
114#if SK_SUPPORT_GPU
115
116#include "effects/GrConstColorProcessor.h"
117#include "effects/GrXfermodeFragmentProcessor.h"
118
119/////////////////////////////////////////////////////////////////////
120
121std::unique_ptr<GrFragmentProcessor> SkComposeShader::asFragmentProcessor(
122        const GrFPArgs& args) const {
123    if (this->isJustMode()) {
124        SkASSERT(fMode != SkBlendMode::kSrc && fMode != SkBlendMode::kDst); // caught in factory
125        if (fMode == SkBlendMode::kClear) {
126            return GrConstColorProcessor::Make(GrColor4f::TransparentBlack(),
127                                               GrConstColorProcessor::InputMode::kIgnore);
128        }
129    }
130
131    std::unique_ptr<GrFragmentProcessor> fpA(as_SB(fDst)->asFragmentProcessor(args));
132    if (!fpA) {
133        return nullptr;
134    }
135    std::unique_ptr<GrFragmentProcessor> fpB(as_SB(fSrc)->asFragmentProcessor(args));
136    if (!fpB) {
137        return nullptr;
138    }
139    // TODO: account for fLerpT when it is < 1
140    return GrXfermodeFragmentProcessor::MakeFromTwoProcessors(std::move(fpB),
141                                                              std::move(fpA), fMode);
142}
143#endif
144
145#ifndef SK_IGNORE_TO_STRING
146void SkComposeShader::toString(SkString* str) const {
147    str->append("SkComposeShader: (");
148
149    str->append("dst: ");
150    as_SB(fDst)->toString(str);
151    str->append(" src: ");
152    as_SB(fSrc)->toString(str);
153    str->appendf(" mode: %s", SkBlendMode_Name(fMode));
154    str->appendf(" lerpT: %g", fLerpT);
155
156    this->INHERITED::toString(str);
157
158    str->append(")");
159}
160#endif
161