1/*
2 * Copyright 2016 Google Inc.
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 "SkColorShader.h"
10#include "SkColorSpace.h"
11#include "SkPM4fPriv.h"
12#include "SkRasterPipeline.h"
13#include "SkReadBuffer.h"
14#include "SkUtils.h"
15
16SkColorShader::SkColorShader(SkColor c) : fColor(c) {}
17
18bool SkColorShader::isOpaque() const {
19    return SkColorGetA(fColor) == 255;
20}
21
22sk_sp<SkFlattenable> SkColorShader::CreateProc(SkReadBuffer& buffer) {
23    return sk_make_sp<SkColorShader>(buffer.readColor());
24}
25
26void SkColorShader::flatten(SkWriteBuffer& buffer) const {
27    buffer.writeColor(fColor);
28}
29
30uint32_t SkColorShader::ColorShaderContext::getFlags() const {
31    return fFlags;
32}
33
34SkShaderBase::Context* SkColorShader::onMakeContext(const ContextRec& rec,
35                                                    SkArenaAlloc* alloc) const {
36    return alloc->make<ColorShaderContext>(*this, rec);
37}
38
39SkColorShader::ColorShaderContext::ColorShaderContext(const SkColorShader& shader,
40                                                      const ContextRec& rec)
41    : INHERITED(shader, rec)
42{
43    SkColor color = shader.fColor;
44    unsigned a = SkAlphaMul(SkColorGetA(color), SkAlpha255To256(rec.fPaint->getAlpha()));
45
46    unsigned r = SkColorGetR(color);
47    unsigned g = SkColorGetG(color);
48    unsigned b = SkColorGetB(color);
49
50    if (a != 255) {
51        r = SkMulDiv255Round(r, a);
52        g = SkMulDiv255Round(g, a);
53        b = SkMulDiv255Round(b, a);
54    }
55    fPMColor = SkPackARGB32(a, r, g, b);
56
57    SkColor4f c4 = SkColor4f::FromColor(shader.fColor);
58    c4.fA *= rec.fPaint->getAlpha() / 255.0f;
59    fPM4f = c4.premul();
60
61    fFlags = kConstInY32_Flag;
62    if (255 == a) {
63        fFlags |= kOpaqueAlpha_Flag;
64    }
65}
66
67void SkColorShader::ColorShaderContext::shadeSpan(int x, int y, SkPMColor span[], int count) {
68    sk_memset32(span, fPMColor, count);
69}
70
71void SkColorShader::ColorShaderContext::shadeSpan4f(int x, int y, SkPM4f span[], int count) {
72    for (int i = 0; i < count; ++i) {
73        span[i] = fPM4f;
74    }
75}
76
77SkShader::GradientType SkColorShader::asAGradient(GradientInfo* info) const {
78    if (info) {
79        if (info->fColors && info->fColorCount >= 1) {
80            info->fColors[0] = fColor;
81        }
82        info->fColorCount = 1;
83        info->fTileMode = SkShader::kRepeat_TileMode;
84    }
85    return kColor_GradientType;
86}
87
88#if SK_SUPPORT_GPU
89
90#include "SkGr.h"
91#include "effects/GrConstColorProcessor.h"
92std::unique_ptr<GrFragmentProcessor> SkColorShader::asFragmentProcessor(
93        const GrFPArgs& args) const {
94    GrColor4f color = SkColorToPremulGrColor4f(fColor, *args.fDstColorSpaceInfo);
95    return GrConstColorProcessor::Make(color, GrConstColorProcessor::InputMode::kModulateA);
96}
97
98#endif
99
100#ifndef SK_IGNORE_TO_STRING
101void SkColorShader::toString(SkString* str) const {
102    str->append("SkColorShader: (");
103
104    str->append("Color: ");
105    str->appendHex(fColor);
106
107    this->INHERITED::toString(str);
108
109    str->append(")");
110}
111#endif
112
113///////////////////////////////////////////////////////////////////////////////////////////////////
114///////////////////////////////////////////////////////////////////////////////////////////////////
115
116static unsigned unit_to_byte(float unit) {
117    SkASSERT(unit >= 0 && unit <= 1);
118    return (unsigned)(unit * 255 + 0.5);
119}
120
121static SkColor unit_to_skcolor(const SkColor4f& unit, SkColorSpace* cs) {
122    return SkColorSetARGB(unit_to_byte(unit.fA), unit_to_byte(unit.fR),
123                          unit_to_byte(unit.fG), unit_to_byte(unit.fB));
124}
125
126SkColor4Shader::SkColor4Shader(const SkColor4f& color, sk_sp<SkColorSpace> space)
127    : fColorSpace(std::move(space))
128    , fColor4(color)
129    , fCachedByteColor(unit_to_skcolor(color.pin(), space.get()))
130{}
131
132sk_sp<SkFlattenable> SkColor4Shader::CreateProc(SkReadBuffer& buffer) {
133    SkColor4f color;
134    buffer.readColor4f(&color);
135    if (buffer.readBool()) {
136        // TODO how do we unflatten colorspaces
137    }
138    return SkShader::MakeColorShader(color, nullptr);
139}
140
141void SkColor4Shader::flatten(SkWriteBuffer& buffer) const {
142    buffer.writeColor4f(fColor4);
143    buffer.writeBool(false);    // TODO how do we flatten colorspaces?
144}
145
146uint32_t SkColor4Shader::Color4Context::getFlags() const {
147    return fFlags;
148}
149
150SkShaderBase::Context* SkColor4Shader::onMakeContext(const ContextRec& rec,
151                                                     SkArenaAlloc* alloc) const {
152    return alloc->make<Color4Context>(*this, rec);
153}
154
155SkColor4Shader::Color4Context::Color4Context(const SkColor4Shader& shader,
156                                                      const ContextRec& rec)
157: INHERITED(shader, rec)
158{
159    SkColor color = shader.fCachedByteColor;
160    unsigned a = SkAlphaMul(SkColorGetA(color), SkAlpha255To256(rec.fPaint->getAlpha()));
161
162    unsigned r = SkColorGetR(color);
163    unsigned g = SkColorGetG(color);
164    unsigned b = SkColorGetB(color);
165
166    if (a != 255) {
167        r = SkMulDiv255Round(r, a);
168        g = SkMulDiv255Round(g, a);
169        b = SkMulDiv255Round(b, a);
170    }
171    fPMColor = SkPackARGB32(a, r, g, b);
172
173    SkColor4f c4 = shader.fColor4;
174    c4.fA *= rec.fPaint->getAlpha() * (1 / 255.0f);
175    fPM4f = c4.premul();
176
177    fFlags = kConstInY32_Flag;
178    if (255 == a) {
179        fFlags |= kOpaqueAlpha_Flag;
180    }
181}
182
183void SkColor4Shader::Color4Context::shadeSpan(int x, int y, SkPMColor span[], int count) {
184    sk_memset32(span, fPMColor, count);
185}
186
187void SkColor4Shader::Color4Context::shadeSpan4f(int x, int y, SkPM4f span[], int count) {
188    for (int i = 0; i < count; ++i) {
189        span[i] = fPM4f;
190    }
191}
192
193// TODO: do we need an updated version of this method for color4+colorspace?
194SkShader::GradientType SkColor4Shader::asAGradient(GradientInfo* info) const {
195    if (info) {
196        if (info->fColors && info->fColorCount >= 1) {
197            info->fColors[0] = fCachedByteColor;
198        }
199        info->fColorCount = 1;
200        info->fTileMode = SkShader::kRepeat_TileMode;
201    }
202    return kColor_GradientType;
203}
204
205#if SK_SUPPORT_GPU
206
207#include "GrColorSpaceInfo.h"
208#include "GrColorSpaceXform.h"
209#include "SkGr.h"
210#include "effects/GrConstColorProcessor.h"
211
212std::unique_ptr<GrFragmentProcessor> SkColor4Shader::asFragmentProcessor(
213        const GrFPArgs& args) const {
214    // Construct an xform assuming float inputs. The color space can have a transfer function on
215    // it, which will be applied below.
216    auto colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(), kRGBA_float_GrPixelConfig,
217                                                   args.fDstColorSpaceInfo->colorSpace());
218    GrColor4f color = GrColor4f::FromSkColor4f(fColor4);
219    if (colorSpaceXform) {
220        color = colorSpaceXform->clampedXform(color);
221    }
222    return GrConstColorProcessor::Make(color.premul(),
223                                       GrConstColorProcessor::InputMode::kModulateA);
224}
225
226#endif
227
228#ifndef SK_IGNORE_TO_STRING
229void SkColor4Shader::toString(SkString* str) const {
230    str->append("SkColor4Shader: (");
231
232    str->append("RGBA:");
233    for (int i = 0; i < 4; ++i) {
234        str->appendf(" %g", fColor4.vec()[i]);
235    }
236    str->append(" )");
237}
238#endif
239
240sk_sp<SkShader> SkColor4Shader::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
241    return SkShader::MakeColorShader(xformer->apply(fCachedByteColor));
242}
243
244sk_sp<SkShader> SkShader::MakeColorShader(const SkColor4f& color, sk_sp<SkColorSpace> space) {
245    if (!SkScalarsAreFinite(color.vec(), 4)) {
246        return nullptr;
247    }
248    return sk_make_sp<SkColor4Shader>(color, std::move(space));
249}
250
251///////////////////////////////////////////////////////////////////////////////////////////////////
252
253bool SkColorShader::onAppendStages(const StageRec& rec) const {
254    rec.fPipeline->append_constant_color(rec.fAlloc, SkPM4f_from_SkColor(fColor, rec.fDstCS));
255    return true;
256}
257
258bool SkColor4Shader::onAppendStages(const StageRec& rec) const {
259    rec.fPipeline->append_constant_color(
260                     rec.fAlloc, to_colorspace(fColor4, fColorSpace.get(), rec.fDstCS).premul());
261    return true;
262}
263