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 "tools/Resources.h"
27
28 #include <math.h>
29
30 // A tint filter maps colors to a given range (gradient), based on the input luminance:
31 //
32 // c' = lerp(lo, hi, luma(c))
33 //
34 // TODO: move to public headers/API?
35 //
MakeTintColorFilter(SkColor lo,SkColor hi)36 static sk_sp<SkColorFilter> MakeTintColorFilter(SkColor lo, SkColor hi) {
37 const auto r_lo = SkColorGetR(lo),
38 g_lo = SkColorGetG(lo),
39 b_lo = SkColorGetB(lo),
40 a_lo = SkColorGetA(lo),
41 r_hi = SkColorGetR(hi),
42 g_hi = SkColorGetG(hi),
43 b_hi = SkColorGetB(hi),
44 a_hi = SkColorGetA(hi);
45
46 // We map component-wise:
47 //
48 // r' = lo.r + (hi.r - lo.r) * luma
49 // g' = lo.g + (hi.g - lo.g) * luma
50 // b' = lo.b + (hi.b - lo.b) * luma
51 // a' = lo.a + (hi.a - lo.a) * luma
52 //
53 // The input luminance is stored in the alpha channel
54 // (and RGB are cleared -- see SkLumaColorFilter). Thus:
55 const float tint_matrix[] = {
56 0, 0, 0, (r_hi - r_lo) / 255.0f, SkIntToScalar(r_lo) / 255.0f,
57 0, 0, 0, (g_hi - g_lo) / 255.0f, SkIntToScalar(g_lo) / 255.0f,
58 0, 0, 0, (b_hi - b_lo) / 255.0f, SkIntToScalar(b_lo) / 255.0f,
59 0, 0, 0, (a_hi - a_lo) / 255.0f, SkIntToScalar(a_lo) / 255.0f,
60 };
61
62 return SkColorFilters::Matrix(tint_matrix)
63 ->makeComposed(SkLumaColorFilter::Make());
64 }
65
66 namespace {
67
68 class MixerCFGM final : public skiagm::GM {
69 public:
MixerCFGM(const SkSize & tileSize,size_t tileCount)70 MixerCFGM(const SkSize& tileSize, size_t tileCount)
71 : fTileSize(tileSize)
72 , fTileCount(tileCount) {}
73
74 protected:
onShortName()75 SkString onShortName() override {
76 return SkString("mixerCF");
77 }
78
onISize()79 SkISize onISize() override {
80 return SkISize::Make(fTileSize.width() * 1.2f * fTileCount,
81 fTileSize.height() * 1.2f * 3); // 3 rows
82 }
83
onDraw(SkCanvas * canvas)84 void onDraw(SkCanvas* canvas) override {
85 SkPaint paint;
86
87 const SkColor gradient_colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorRED };
88 paint.setShader(SkGradientShader::MakeSweep(fTileSize.width() / 2,
89 fTileSize.height() / 2,
90 gradient_colors, nullptr,
91 SK_ARRAY_COUNT(gradient_colors)));
92
93 auto cf0 = MakeTintColorFilter(0xff300000, 0xffa00000); // red tint
94 auto cf1 = MakeTintColorFilter(0xff003000, 0xff00a000); // green tint
95
96 this->mixRow(canvas, paint, nullptr, cf1);
97 this->mixRow(canvas, paint, cf0, nullptr);
98 this->mixRow(canvas, paint, cf0, cf1);
99 }
100
101 private:
102 const SkSize fTileSize;
103 const size_t fTileCount;
104
mixRow(SkCanvas * canvas,SkPaint & paint,sk_sp<SkColorFilter> cf0,sk_sp<SkColorFilter> cf1)105 void mixRow(SkCanvas* canvas, SkPaint& paint,
106 sk_sp<SkColorFilter> cf0, sk_sp<SkColorFilter> cf1) {
107 canvas->translate(0, fTileSize.height() * 0.1f);
108 {
109 SkAutoCanvasRestore arc(canvas, true);
110 for (size_t i = 0; i < fTileCount; ++i) {
111 paint.setColorFilter(
112 SkColorFilters::Lerp(static_cast<float>(i) / (fTileCount - 1), cf0, cf1));
113 canvas->translate(fTileSize.width() * 0.1f, 0);
114 canvas->drawRect(SkRect::MakeWH(fTileSize.width(), fTileSize.height()), paint);
115 canvas->translate(fTileSize.width() * 1.1f, 0);
116 }
117 }
118 canvas->translate(0, fTileSize.height() * 1.1f);
119 }
120
121 using INHERITED = skiagm::GM;
122 };
123
124 } // namespace
125 DEF_GM( return new MixerCFGM(SkSize::Make(200, 250), 5); )
126
make_resource_shader(const char path[],int size)127 static sk_sp<SkShader> make_resource_shader(const char path[], int size) {
128 auto img = GetResourceAsImage(path);
129 if (!img) {
130 return nullptr;
131 }
132 SkRect src = SkRect::MakeIWH(img->width(), img->height());
133 SkRect dst = SkRect::MakeIWH(size, size);
134 SkMatrix m;
135 m.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
136 return img->makeShader(&m);
137 }
138
make_grad(int size,float t)139 static sk_sp<SkShader> make_grad(int size, float t) {
140 SkASSERT(t >= 0 && t <= 1);
141 unsigned r = SkScalarRoundToInt(t * 255);
142 SkColor c = SkColorSetARGB(r, r, 0, 0);
143
144 SkColor colors[] = { 0, c, SK_ColorRED };
145 SkPoint pts[] = {{0, 0}, {size*1.0f, size*1.0f}};
146 SkScalar pos[] = {0, 1 - t, 1.0f};
147 return SkGradientShader::MakeLinear(pts, colors, pos, SK_ARRAY_COUNT(colors),
148 SkTileMode::kClamp);
149 }
150
151 class ShaderMixerGM final : public skiagm::GM {
152 enum { SIZE = 256 };
153 float fPos = 0.5f;
154 sk_sp<SkShader> fS0, fS1;
155
156 public:
ShaderMixerGM()157 ShaderMixerGM() {}
158
159 protected:
onShortName()160 SkString onShortName() override {
161 return SkString("mixershader_shadermixer");
162 }
163
onOnceBeforeDraw()164 void onOnceBeforeDraw() override {
165 fS0 = make_resource_shader("images/mandrill_256.png", SIZE);
166 fS1 = make_resource_shader("images/baby_tux.png", SIZE);
167 }
168
onISize()169 SkISize onISize() override { return {542, 542}; }
170
onDraw(SkCanvas * canvas)171 void onDraw(SkCanvas* canvas) override {
172 SkRect r = SkRect::MakeIWH(SIZE, SIZE);
173
174 SkPaint paint;
175
176 canvas->translate(10, 10);
177
178 canvas->save();
179 paint.setShader(fS0);
180 canvas->drawRect(r, paint);
181 canvas->translate(SIZE + 10.0f, 0);
182 paint.setShader(fS1);
183 canvas->drawRect(r, paint);
184 canvas->restore();
185
186 auto sh2 = make_grad(SIZE, fPos);
187
188 canvas->translate(0, SIZE + 10.0f);
189 paint.setShader(sh2);
190 canvas->drawRect(r, paint);
191
192 auto sh = SkShaders::Lerp(sh2, fS0, fS1);
193 canvas->translate(SIZE + 10.0f, 0);
194 paint.setShader(sh);
195 canvas->drawRect(r, paint);
196 }
197
onAnimate(double nanos)198 bool onAnimate(double nanos) override {
199 fPos = (sin(1e-9 * nanos) + 1) * 0.5f;
200 return true;
201 }
202
203 private:
204 using INHERITED = skiagm::GM;
205 };
206 DEF_GM( return new ShaderMixerGM; )
207