1 /*
2 * Copyright 2021 Google LLC
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/SkBitmap.h"
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkColorFilter.h"
12 #include "include/core/SkColorSpace.h"
13 #include "include/core/SkImage.h"
14 #include "include/core/SkPaint.h"
15 #include "include/core/SkRRect.h"
16 #include "include/effects/SkGradientShader.h"
17 #include "include/gpu/GrRecordingContext.h"
18 #include "src/core/SkColorFilterPriv.h"
19 #include "tools/Resources.h"
20 #include "tools/ToolUtils.h"
21
22 namespace {
23
create_gradient_shader(SkRect r,const std::array<SkColor,3> & colors,const std::array<float,3> & offsets)24 sk_sp<SkShader> create_gradient_shader(SkRect r,
25 const std::array<SkColor, 3>& colors,
26 const std::array<float, 3>& offsets) {
27 SkPoint pts[2] = { {r.fLeft, r.fTop}, {r.fRight, r.fTop} };
28
29 return SkGradientShader::MakeLinear(pts, colors.data(), offsets.data(), std::size(colors),
30 SkTileMode::kClamp);
31 }
32
create_image_shader(SkCanvas * destCanvas,SkTileMode tmX,SkTileMode tmY)33 sk_sp<SkShader> create_image_shader(SkCanvas* destCanvas, SkTileMode tmX, SkTileMode tmY) {
34 SkBitmap bitmap;
35
36 {
37 SkImageInfo ii = SkImageInfo::Make(64, 64, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
38 bitmap.allocPixels(ii);
39 bitmap.eraseColor(SK_ColorWHITE);
40
41 SkCanvas tmpCanvas(bitmap);
42
43 SkColor colors[3][3] = {
44 { SK_ColorRED, SK_ColorDKGRAY, SK_ColorBLUE },
45 { SK_ColorLTGRAY, SK_ColorCYAN, SK_ColorYELLOW },
46 { SK_ColorGREEN, SK_ColorWHITE, SK_ColorMAGENTA }
47 };
48
49 for (int y = 0; y < 3; ++y) {
50 for (int x = 0; x < 3; ++x) {
51 SkPaint paint;
52 paint.setColor(colors[y][x]);
53 tmpCanvas.drawRect(SkRect::MakeXYWH(x*21, y*21, 22, 22), paint);
54 }
55 }
56
57 bitmap.setAlphaType(kOpaque_SkAlphaType);
58 bitmap.setImmutable();
59 }
60
61 sk_sp<SkImage> img = SkImage::MakeFromBitmap(bitmap);
62 img = ToolUtils::MakeTextureImage(destCanvas, std::move(img));
63 if (img) {
64 return img->makeShader(tmX, tmY, SkSamplingOptions());
65 } else {
66 return nullptr;
67 }
68 }
69
create_blend_shader(SkCanvas * destCanvas,SkBlendMode bm)70 sk_sp<SkShader> create_blend_shader(SkCanvas* destCanvas, SkBlendMode bm) {
71 constexpr SkColor4f kTransYellow = {1.0f, 1.0f, 0.0f, 0.5f};
72
73 sk_sp<SkShader> dst = SkShaders::Color(kTransYellow, nullptr);
74 return SkShaders::Blend(bm,
75 std::move(dst),
76 create_image_shader(destCanvas,
77 SkTileMode::kRepeat, SkTileMode::kRepeat));
78 }
79
create_grayscale_colorfilter()80 sk_sp<SkColorFilter> create_grayscale_colorfilter() {
81 float matrix[20] = {};
82 matrix[0] = matrix[5] = matrix[10] = 0.2126f;
83 matrix[1] = matrix[6] = matrix[11] = 0.7152f;
84 matrix[2] = matrix[7] = matrix[12] = 0.0722f;
85 matrix[18] = 1.0f;
86 return SkColorFilters::Matrix(matrix);
87 }
88
draw_image_shader_tile(SkCanvas * canvas,SkRect clipRect)89 void draw_image_shader_tile(SkCanvas* canvas, SkRect clipRect) {
90 SkPaint p;
91 p.setShader(create_image_shader(canvas, SkTileMode::kClamp, SkTileMode::kRepeat));
92
93 SkPath path;
94 path.moveTo(1, 1);
95 path.lineTo(32, 127);
96 path.lineTo(96, 127);
97 path.lineTo(127, 1);
98 path.lineTo(63, 32);
99 path.close();
100
101 canvas->save();
102 canvas->clipRect(clipRect);
103 canvas->scale(0.5f, 0.5f);
104 canvas->drawPath(path, p);
105
106 canvas->save();
107 canvas->concat(SkMatrix::RotateDeg(90, {64, 64}));
108 canvas->translate(128, 0);
109 canvas->drawPath(path, p);
110 canvas->restore();
111 canvas->restore();
112 }
113
draw_gradient_tile(SkCanvas * canvas,SkRect clipRect)114 void draw_gradient_tile(SkCanvas* canvas, SkRect clipRect) {
115 SkRect r{1, 1, 127, 127};
116 SkPaint p;
117 p.setShader(create_gradient_shader(r,
118 { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE },
119 { 0.0f, 0.75f, 1.0f }));
120
121 canvas->save();
122 canvas->clipRect(clipRect);
123 canvas->translate(128, 0);
124 canvas->scale(0.5f, 0.5f);
125 canvas->drawRect(r, p);
126
127 canvas->save();
128 canvas->concat(SkMatrix::RotateDeg(90, {64, 64}));
129 canvas->translate(128, 0);
130 canvas->drawRect(r, p);
131 canvas->restore();
132 canvas->restore();
133 }
134
draw_colorfilter_swatches(SkCanvas * canvas,SkRect clipRect)135 void draw_colorfilter_swatches(SkCanvas* canvas, SkRect clipRect) {
136 static constexpr int kNumTilesPerSide = 3;
137
138 SkSize tileSize = { clipRect.width() / kNumTilesPerSide, clipRect.height() / kNumTilesPerSide };
139
140 // Quantize to four colors
141 uint8_t table1[256];
142 for (int i = 0; i < 256; ++i) {
143 table1[i] = (i/64) * 85;
144 }
145
146 // table2 is a band-pass filter for 85-170.
147 // table3 re-expands that range to 0..255
148 uint8_t table2[256], table3[256];
149 for (int i = 0; i < 256; ++i) {
150 if (i >= 85 && i <= 170) {
151 table2[i] = i;
152 table3[i] = ((i - 85) / 85.0f) * 255.0f;
153 } else {
154 table2[i] = 0;
155 table3[i] = 0;
156 }
157 }
158
159 constexpr SkColor SK_ColorGREY = SkColorSetARGB(0xFF, 0x80, 0x80, 0x80);
160
161 sk_sp<SkColorFilter> colorFilters[kNumTilesPerSide*kNumTilesPerSide];
162 static const std::array<SkColor, 3> kGradientColors[kNumTilesPerSide*kNumTilesPerSide] = {
163 { SK_ColorBLACK, SK_ColorGREY, SK_ColorWHITE },
164 { SK_ColorBLACK, SK_ColorGREY, SK_ColorWHITE },
165 { SK_ColorBLACK, SK_ColorGREY, SK_ColorWHITE },
166 { SK_ColorBLACK, SK_ColorGREY, SK_ColorWHITE },
167 { 0x00000000, 0x80000000, 0xFF000000 }, // the Gaussian CF uses alpha only
168 { SK_ColorBLACK, SK_ColorGREY, SK_ColorWHITE },
169 { SK_ColorBLACK, SK_ColorGREY, SK_ColorWHITE },
170 { SK_ColorBLACK, SK_ColorGREY, SK_ColorWHITE },
171 { SK_ColorBLACK, SK_ColorGREY, SK_ColorWHITE },
172 };
173
174 colorFilters[0] = SkColorFilters::Lighting(SK_ColorLTGRAY, 0xFF440000);
175 colorFilters[1] = SkColorFilters::Table(table1);
176 colorFilters[2] = SkColorFilters::Compose(SkColorFilters::TableARGB(nullptr, table3,
177 table3, table3),
178 SkColorFilters::TableARGB(nullptr, table2,
179 table2, table2));
180 colorFilters[3] = SkColorFilters::Blend(SK_ColorGREEN, SkBlendMode::kMultiply);
181 colorFilters[4] = SkColorFilterPriv::MakeGaussian();
182
183 colorFilters[5] = SkColorFilters::LinearToSRGBGamma();
184 colorFilters[6] = SkColorFilters::SRGBToLinearGamma();
185
186 SkPaint p;
187
188 canvas->save();
189 canvas->clipRect(clipRect);
190 canvas->translate(clipRect.fLeft, clipRect.fTop);
191
192 for (int y = 0; y < kNumTilesPerSide; ++y) {
193 for (int x = 0; x < kNumTilesPerSide; ++x) {
194 SkRect r = SkRect::MakeXYWH(x * tileSize.width(), y * tileSize.height(),
195 tileSize.width(), tileSize.height()).makeInset(1.0f,
196 1.0f);
197 int colorFilterIndex = x*kNumTilesPerSide+y;
198 p.setShader(create_gradient_shader(r,
199 kGradientColors[colorFilterIndex],
200 { 0.0f, 0.5f, 1.0f }));
201 p.setColorFilter(colorFilters[colorFilterIndex]);
202 canvas->drawRect(r, p);
203 }
204 }
205
206 canvas->restore();
207 }
208
draw_blend_mode_swatches(SkCanvas * canvas,SkRect clipRect)209 void draw_blend_mode_swatches(SkCanvas* canvas, SkRect clipRect) {
210 static const int kTileHeight = 16;
211 static const int kTileWidth = 16;
212 static const SkColor4f kOpaqueWhite { 1.0f, 1.0f, 1.0f, 1.0f };
213 static const SkColor4f kTransBluish { 0.0f, 0.5f, 1.0f, 0.5f };
214 static const SkColor4f kTransWhite { 1.0f, 1.0f, 1.0f, 0.75f };
215
216 SkPaint dstPaint;
217 dstPaint.setColor(kOpaqueWhite);
218 dstPaint.setBlendMode(SkBlendMode::kSrc);
219 dstPaint.setAntiAlias(false);
220
221 SkPaint srcPaint;
222 srcPaint.setColor(kTransBluish);
223 srcPaint.setAntiAlias(false);
224
225 SkRect r = SkRect::MakeXYWH(clipRect.fLeft, clipRect.fTop, kTileWidth, kTileHeight);
226
227 // For the first pass we draw: transparent bluish on top of opaque white
228 // For the second pass we draw: transparent white on top of transparent bluish
229 for (int passes = 0; passes < 2; ++passes) {
230 for (int i = 0; i <= (int)SkBlendMode::kLastCoeffMode; ++i) {
231 if (r.fLeft+kTileWidth > clipRect.fRight) {
232 r.offsetTo(clipRect.fLeft, r.fTop+kTileHeight);
233 }
234
235 canvas->drawRect(r.makeInset(1.0f, 1.0f), dstPaint);
236 srcPaint.setBlendMode(static_cast<SkBlendMode>(i));
237 canvas->drawRect(r.makeInset(2.0f, 2.0f), srcPaint);
238
239 r.offset(kTileWidth, 0.0f);
240 }
241
242 r.offsetTo(clipRect.fLeft, r.fTop+kTileHeight);
243 srcPaint.setColor(kTransWhite);
244 dstPaint.setColor(kTransBluish);
245 }
246 }
247
248 } // anonymous namespace
249
250 namespace skiagm {
251
252 // This is just for bootstrapping Graphite.
253 class GraphiteStartGM : public GM {
254 public:
GraphiteStartGM()255 GraphiteStartGM() {
256 this->setBGColor(SK_ColorBLACK);
257 GetResourceAsBitmap("images/color_wheel.gif", &fBitmap);
258 }
259
260 protected:
261 static constexpr int kTileWidth = 128;
262 static constexpr int kTileHeight = 128;
263 static constexpr int kWidth = 3 * kTileWidth;
264 static constexpr int kHeight = 3 * kTileHeight;
265 static constexpr int kClipInset = 4;
266
onShortName()267 SkString onShortName() override {
268 return SkString("graphitestart");
269 }
270
onISize()271 SkISize onISize() override {
272 return SkISize::Make(kWidth, kHeight);
273 }
274
onDraw(SkCanvas * canvas)275 void onDraw(SkCanvas* canvas) override {
276
277 const SkRect clipRect = SkRect::MakeWH(kWidth, kHeight).makeInset(kClipInset, kClipInset);
278
279 canvas->save();
280 canvas->clipRRect(SkRRect::MakeRectXY(clipRect, 32.f, 32.f), true);
281
282 // Upper-left corner
283 draw_image_shader_tile(canvas, SkRect::MakeXYWH(0, 0, kTileWidth, kTileHeight));
284
285 // Upper-middle tile
286 draw_gradient_tile(canvas, SkRect::MakeXYWH(kTileWidth, 0, kTileWidth, kTileHeight));
287
288 // Upper-right corner
289 draw_colorfilter_swatches(canvas, SkRect::MakeXYWH(2*kTileWidth, 0,
290 kTileWidth, kTileWidth));
291
292 // Middle-left tile
293 {
294 SkPaint p;
295 p.setColor(SK_ColorRED);
296
297 SkRect r = SkRect::MakeXYWH(0, kTileHeight, kTileWidth, kTileHeight);
298 canvas->drawRect(r.makeInset(1.0f, 1.0f), p);
299 }
300
301 // Middle-middle tile
302 {
303 SkPaint p;
304 p.setShader(create_blend_shader(canvas, SkBlendMode::kModulate));
305
306 SkRect r = SkRect::MakeXYWH(kTileWidth, kTileHeight, kTileWidth, kTileHeight);
307 canvas->drawRect(r.makeInset(1.0f, 1.0f), p);
308 }
309
310 // Middle-right tile
311 {
312 sk_sp<SkImage> image(GetResourceAsImage("images/mandrill_128.png"));
313 sk_sp<SkShader> shader;
314
315 if (image) {
316 shader = image->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat, {});
317 shader = shader->makeWithColorFilter(create_grayscale_colorfilter());
318 }
319
320 SkPaint p;
321 p.setShader(std::move(shader));
322
323 SkRect r = SkRect::MakeXYWH(2*kTileWidth, kTileHeight, kTileWidth, kTileHeight);
324 canvas->drawRect(r.makeInset(1.0f, 1.0f), p);
325 }
326
327 canvas->restore();
328
329 // Bottom-left corner
330 #if defined(SK_GRAPHITE)
331 // TODO: failing serialize test on Linux, not sure what's going on
332 canvas->writePixels(fBitmap, 0, 2*kTileHeight);
333 #endif
334
335 // Bottom-middle tile
336 draw_blend_mode_swatches(canvas, SkRect::MakeXYWH(kTileWidth, 2*kTileHeight,
337 kTileWidth, kTileHeight));
338
339 // Bottom-right corner
340 {
341 const SkRect kTile = SkRect::MakeXYWH(2*kTileWidth, 2*kTileHeight,
342 kTileWidth, kTileHeight);
343
344 SkPaint circlePaint;
345 circlePaint.setColor(SK_ColorBLUE);
346 circlePaint.setBlendMode(SkBlendMode::kSrc);
347
348 canvas->clipRect(kTile);
349 canvas->drawRect(kTile.makeInset(10, 20), circlePaint);
350
351 SkPaint restorePaint;
352 restorePaint.setBlendMode(SkBlendMode::kPlus);
353
354 canvas->saveLayer(nullptr, &restorePaint);
355 circlePaint.setColor(SK_ColorRED);
356 circlePaint.setBlendMode(SkBlendMode::kSrc);
357
358 canvas->drawRect(kTile.makeInset(15, 25), circlePaint);
359 canvas->restore();
360 }
361 }
362
363 private:
364 SkBitmap fBitmap;
365 };
366
367 //////////////////////////////////////////////////////////////////////////////
368
369 DEF_GM(return new GraphiteStartGM;)
370
371 } // namespace skiagm
372