1 /*
2 * Copyright 2019 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 "gm/gm.h"
9 #include "include/core/SkCanvas.h"
10 #include "include/core/SkColor.h"
11 #include "include/core/SkColorFilter.h"
12 #include "include/core/SkImage.h"
13 #include "include/core/SkMatrix.h"
14 #include "include/core/SkPaint.h"
15 #include "include/core/SkPoint.h"
16 #include "include/core/SkRect.h"
17 #include "include/core/SkRefCnt.h"
18 #include "include/core/SkScalar.h"
19 #include "include/core/SkShader.h"
20 #include "include/core/SkSize.h"
21 #include "include/core/SkString.h"
22 #include "include/core/SkTileMode.h"
23 #include "include/core/SkTypes.h"
24 #include "include/effects/SkGradientShader.h"
25 #include "include/effects/SkLumaColorFilter.h"
26 #include "include/effects/SkRuntimeEffect.h"
27 #include "tools/Resources.h"
28
29 #include <math.h>
30
31 // A tint filter maps colors to a given range (gradient), based on the input luminance:
32 //
33 // c' = lerp(lo, hi, luma(c))
34 //
35 // TODO: move to public headers/API?
36 //
MakeTintColorFilter(SkColor lo,SkColor hi)37 static sk_sp<SkColorFilter> MakeTintColorFilter(SkColor lo, SkColor hi) {
38 const auto r_lo = SkColorGetR(lo),
39 g_lo = SkColorGetG(lo),
40 b_lo = SkColorGetB(lo),
41 a_lo = SkColorGetA(lo),
42 r_hi = SkColorGetR(hi),
43 g_hi = SkColorGetG(hi),
44 b_hi = SkColorGetB(hi),
45 a_hi = SkColorGetA(hi);
46
47 // We map component-wise:
48 //
49 // r' = lo.r + (hi.r - lo.r) * luma
50 // g' = lo.g + (hi.g - lo.g) * luma
51 // b' = lo.b + (hi.b - lo.b) * luma
52 // a' = lo.a + (hi.a - lo.a) * luma
53 //
54 // The input luminance is stored in the alpha channel
55 // (and RGB are cleared -- see SkLumaColorFilter). Thus:
56 const float tint_matrix[] = {
57 0, 0, 0, (r_hi - r_lo) / 255.0f, SkIntToScalar(r_lo) / 255.0f,
58 0, 0, 0, (g_hi - g_lo) / 255.0f, SkIntToScalar(g_lo) / 255.0f,
59 0, 0, 0, (b_hi - b_lo) / 255.0f, SkIntToScalar(b_lo) / 255.0f,
60 0, 0, 0, (a_hi - a_lo) / 255.0f, SkIntToScalar(a_lo) / 255.0f,
61 };
62
63 return SkColorFilters::Matrix(tint_matrix)->makeComposed(SkLumaColorFilter::Make());
64 }
65
66 namespace {
67
68 class MixerCFGM final : public skiagm::GM {
69 public:
MixerCFGM(const SkSize & tileSize,size_t tileCount,bool runtime)70 MixerCFGM(const SkSize& tileSize, size_t tileCount, bool runtime)
71 : fTileSize(tileSize)
72 , fTileCount(tileCount)
73 , fRuntime(runtime) {}
74
75 protected:
onShortName()76 SkString onShortName() override {
77 return fRuntime ? SkString("mixerCF_runtime")
78 : SkString("mixerCF");
79 }
80
onISize()81 SkISize onISize() override {
82 return SkISize::Make(fTileSize.width() * 1.2f * fTileCount,
83 fTileSize.height() * 1.2f * 3); // 3 rows
84 }
85
onDraw(SkCanvas * canvas)86 void onDraw(SkCanvas* canvas) override {
87 SkPaint paint;
88
89 const SkColor gradient_colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorRED };
90 paint.setShader(SkGradientShader::MakeSweep(fTileSize.width() / 2,
91 fTileSize.height() / 2,
92 gradient_colors, nullptr,
93 SK_ARRAY_COUNT(gradient_colors)));
94
95 auto cf0 = MakeTintColorFilter(0xff300000, 0xffa00000); // red tint
96 auto cf1 = MakeTintColorFilter(0xff003000, 0xff00a000); // green tint
97
98 this->mixRow(canvas, paint, nullptr, cf1);
99 this->mixRow(canvas, paint, cf0, nullptr);
100 this->mixRow(canvas, paint, cf0, cf1);
101 }
102
103 private:
104 const SkSize fTileSize;
105 const size_t fTileCount;
106 const bool fRuntime;
107
mixRow(SkCanvas * canvas,SkPaint & paint,sk_sp<SkColorFilter> cf0,sk_sp<SkColorFilter> cf1)108 void mixRow(SkCanvas* canvas, SkPaint& paint,
109 sk_sp<SkColorFilter> cf0, sk_sp<SkColorFilter> cf1) {
110 sk_sp<SkRuntimeEffect> effect;
111 if (fRuntime) {
112 const char* sksl = R"(
113 uniform shader cf0;
114 uniform shader cf1;
115 uniform half t;
116 half4 main() {
117 return mix(sample(cf0), sample(cf1), t);
118 }
119 )";
120 effect = std::get<0>(SkRuntimeEffect::Make(SkString(sksl)));
121 SkASSERT(effect);
122 }
123
124 // We cycle through paint colors on each row, to test how the paint color flows through
125 // the color-filter network
126 const SkColor4f paintColors[] = {
127 { 1.0f, 1.0f, 1.0f, 1.0f }, // Opaque white
128 { 1.0f, 1.0f, 1.0f, 0.5f }, // Translucent white
129 { 0.5f, 0.5f, 1.0f, 1.0f }, // Opaque pale blue
130 { 0.5f, 0.5f, 1.0f, 0.5f }, // Translucent pale blue
131 };
132
133 canvas->translate(0, fTileSize.height() * 0.1f);
134 {
135 SkAutoCanvasRestore arc(canvas, true);
136 for (size_t i = 0; i < fTileCount; ++i) {
137 paint.setColor4f(paintColors[i % SK_ARRAY_COUNT(paintColors)]);
138 float t = static_cast<float>(i) / (fTileCount - 1);
139 if (fRuntime) {
140 sk_sp<SkColorFilter> children[] = { cf0, cf1 };
141 sk_sp<SkData> inputs = SkData::MakeWithCopy(&t, sizeof(t));
142 paint.setColorFilter(effect->makeColorFilter(inputs, children, 2));
143 } else {
144 paint.setColorFilter(SkColorFilters::Lerp(t, cf0, cf1));
145 }
146 canvas->translate(fTileSize.width() * 0.1f, 0);
147 canvas->drawRect(SkRect::MakeWH(fTileSize.width(), fTileSize.height()), paint);
148 canvas->translate(fTileSize.width() * 1.1f, 0);
149 }
150 }
151 canvas->translate(0, fTileSize.height() * 1.1f);
152 }
153
154 using INHERITED = skiagm::GM;
155 };
156
157 } // namespace
158
159 DEF_GM( return new MixerCFGM(SkSize::Make(200, 250), 5, false); )
160 DEF_GM( return new MixerCFGM(SkSize::Make(200, 250), 5, true); )
161