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