blob: 68b179e1ac3e5f0c4037d112048650f839ffd68a [file] [log] [blame]
reed1a9caff2015-09-02 19:05:10 -07001/*
2 * Copyright 2015 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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkCanvas.h"
9#include "include/core/SkDrawable.h"
10#include "include/core/SkPath.h"
11#include "include/core/SkRSXform.h"
12#include "include/core/SkString.h"
13#include "include/core/SkSurface.h"
14#include "include/effects/SkGradientShader.h"
15#include "include/utils/SkRandom.h"
16#include "include/utils/SkTextUtils.h"
17#include "samplecode/Sample.h"
reed1a9caff2015-09-02 19:05:10 -070018
Brian Osmand1e67e72017-03-15 12:19:37 -040019const SkBlendMode gModes[] = {
20 SkBlendMode::kSrcOver,
21 SkBlendMode::kSrc,
22 SkBlendMode::kSrcIn,
23 SkBlendMode::kSrcOut,
24 SkBlendMode::kSrcATop,
25 SkBlendMode::kDstOver,
26 SkBlendMode::kDstIn,
27 SkBlendMode::kDstOut,
28 SkBlendMode::kDstATop,
reed1a9caff2015-09-02 19:05:10 -070029};
30const int N_Modes = SK_ARRAY_COUNT(gModes);
31
reed1a9caff2015-09-02 19:05:10 -070032static SkRandom gRand;
33
Brian Osmanede860e2017-11-22 16:36:07 -050034struct ModeButton {
reed1a9caff2015-09-02 19:05:10 -070035 SkString fLabel;
36 SkColor fColor;
Brian Osmanede860e2017-11-22 16:36:07 -050037 SkRect fRect;
reed1a9caff2015-09-02 19:05:10 -070038
39public:
Brian Osmanede860e2017-11-22 16:36:07 -050040 void init(const char label[], const SkRect& rect) {
41 fLabel = label;
42 fRect = rect;
reed1a9caff2015-09-02 19:05:10 -070043 fColor = (gRand.nextU() & 0x7F7F7F7F) | SkColorSetARGB(0xFF, 0, 0, 0x80);
reed1a9caff2015-09-02 19:05:10 -070044 }
halcanary9d524f22016-03-29 09:03:52 -070045
Brian Osmanede860e2017-11-22 16:36:07 -050046 void draw(SkCanvas* canvas) {
reed1a9caff2015-09-02 19:05:10 -070047 SkPaint paint;
48 paint.setAntiAlias(true);
49 paint.setColor(fColor);
Brian Osmanede860e2017-11-22 16:36:07 -050050 canvas->drawRoundRect(fRect, 8, 8, paint);
reed1a9caff2015-09-02 19:05:10 -070051
52 paint.setColor(0xFFFFFFFF);
Mike Reedb579f072019-01-03 15:45:53 -050053 SkFont font;
54 font.setSize(16);
55 font.setEdging(SkFont::Edging::kSubpixelAntiAlias);
56 SkTextUtils::DrawString(canvas, fLabel.c_str(), fRect.centerX(), fRect.fTop + 0.68f * fRect.height(),
57 font, paint, SkTextUtils::kCenter_Align);
reed1a9caff2015-09-02 19:05:10 -070058 }
59
Brian Osmanede860e2017-11-22 16:36:07 -050060 bool hitTest(SkScalar x, SkScalar y) {
Mike Reed9ea63152019-08-22 16:19:50 -040061 return fRect.intersects({x - 1, y - 1, x + 1, y + 1});
reed1a9caff2015-09-02 19:05:10 -070062 }
reed1a9caff2015-09-02 19:05:10 -070063};
64
reed1a9caff2015-09-02 19:05:10 -070065class ModeDrawable : public SkDrawable {
66public:
reed374772b2016-10-05 17:33:02 -070067 ModeDrawable() : fMode(SkBlendMode::kSrcOver), fLoc(SkPoint::Make(0, 0)) {}
reed1a9caff2015-09-02 19:05:10 -070068
reed374772b2016-10-05 17:33:02 -070069 SkBlendMode fMode;
70 SkPoint fLoc;
reed1a9caff2015-09-02 19:05:10 -070071
72 bool hitTest(SkScalar x, SkScalar y) {
73 SkRect target = SkRect::MakeXYWH(x - fLoc.x() - 1, y - fLoc.y() - 1, 3, 3);
74 return this->getBounds().intersects(target);
75 }
76};
77
78class CircDrawable : public ModeDrawable {
79 SkPaint fPaint;
80 SkRect fBounds;
81
82public:
83 CircDrawable(SkScalar size, SkColor c) {
84 const SkColor colors[] = { 0, c };
reed8a21c9f2016-03-08 18:50:00 -080085 fPaint.setShader(SkGradientShader::MakeRadial(SkPoint::Make(size/2, size/2), size/2,
reed1a9caff2015-09-02 19:05:10 -070086 colors, nullptr, 2,
Mike Reedfae8fce2019-04-03 10:27:45 -040087 SkTileMode::kClamp));
reed1a9caff2015-09-02 19:05:10 -070088 fBounds = SkRect::MakeWH(size, size);
89 }
90
91protected:
92 SkRect onGetBounds() override {
93 return fBounds;
94 }
95
96 void onDraw(SkCanvas* canvas) override {
reed374772b2016-10-05 17:33:02 -070097 fPaint.setBlendMode(fMode);
reed1a9caff2015-09-02 19:05:10 -070098 canvas->save();
99 canvas->translate(fLoc.x(), fLoc.y());
100 canvas->drawOval(fBounds, fPaint);
101 canvas->restore();
102 }
103};
104
Ben Wagnerb2c4ea62018-08-08 11:36:17 -0400105class XferDemo : public Sample {
reed1a9caff2015-09-02 19:05:10 -0700106 enum {
107 N = 4
108 };
halcanary9d524f22016-03-29 09:03:52 -0700109
reed1a9caff2015-09-02 19:05:10 -0700110 SkRect fModeRect[N_Modes];
Brian Osmanede860e2017-11-22 16:36:07 -0500111 ModeButton fModeButtons[N_Modes];
Hal Canary2db83612016-11-04 13:02:54 -0400112 sk_sp<CircDrawable> fDrs[N];
reed1a9caff2015-09-02 19:05:10 -0700113 CircDrawable* fSelected;
114
115 void addButtons() {
116 SkScalar x = 10;
117 SkScalar y = 10;
118 for (int i = 0; i < N_Modes; ++i) {
Brian Osmanede860e2017-11-22 16:36:07 -0500119 fModeButtons[i].init(SkBlendMode_Name(gModes[i]), SkRect::MakeXYWH(x, y, 70, 25));
reed1a9caff2015-09-02 19:05:10 -0700120 fModeRect[i] = SkRect::MakeXYWH(x, y + 28, 70, 2);
121 x += 80;
122 }
123 }
124
125public:
126 XferDemo() {
127 const SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorBLACK };
128 for (int i = 0; i < N; ++i) {
129 fDrs[i].reset(new CircDrawable(200, colors[i]));
130 fDrs[i]->fLoc.set(100.f + i * 100, 100.f + i * 100);
reed374772b2016-10-05 17:33:02 -0700131 fDrs[i]->fMode = SkBlendMode::kSrcOver;
reed1a9caff2015-09-02 19:05:10 -0700132 }
133 fSelected = nullptr;
134
135 this->addButtons();
136 }
137
138protected:
Hal Canary8a027312019-07-03 10:55:44 -0400139 SkString name() override { return SkString("XferDemo"); }
reed1a9caff2015-09-02 19:05:10 -0700140
141 void onDrawContent(SkCanvas* canvas) override {
Brian Osmanede860e2017-11-22 16:36:07 -0500142 for (int i = 0; i < N_Modes; ++i) {
143 fModeButtons[i].draw(canvas);
144 }
145
reed1a9caff2015-09-02 19:05:10 -0700146 SkPaint paint;
147 if (fSelected) {
148 for (int i = 0; i < N_Modes; ++i) {
Brian Osmand1e67e72017-03-15 12:19:37 -0400149 if (fSelected->fMode == gModes[i]) {
reed1a9caff2015-09-02 19:05:10 -0700150 canvas->drawRect(fModeRect[i], paint);
151 break;
152 }
153 }
154 }
155
156 canvas->saveLayer(nullptr, nullptr);
157 for (int i = 0; i < N; ++i) {
158 fDrs[i]->draw(canvas);
159 }
160 canvas->restore();
161 }
162
Hal Canaryb1f411a2019-08-29 10:39:22 -0400163 Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) override {
Brian Osmanede860e2017-11-22 16:36:07 -0500164 // Check mode buttons first
165 for (int i = 0; i < N_Modes; ++i) {
166 if (fModeButtons[i].hitTest(x, y)) {
Hal Canaryfcf63592019-07-12 11:32:43 -0400167 Click* click = new Click();
Brian Osmanede860e2017-11-22 16:36:07 -0500168 click->fMeta.setS32("mode", i);
169 return click;
170 }
171 }
reed1a9caff2015-09-02 19:05:10 -0700172 fSelected = nullptr;
173 for (int i = N - 1; i >= 0; --i) {
174 if (fDrs[i]->hitTest(x, y)) {
Hal Canary2db83612016-11-04 13:02:54 -0400175 fSelected = fDrs[i].get();
reed1a9caff2015-09-02 19:05:10 -0700176 break;
177 }
178 }
Hal Canaryfcf63592019-07-12 11:32:43 -0400179 return fSelected ? new Click() : nullptr;
reed1a9caff2015-09-02 19:05:10 -0700180 }
halcanary9d524f22016-03-29 09:03:52 -0700181
reed1a9caff2015-09-02 19:05:10 -0700182 bool onClick(Click* click) override {
Brian Osmanede860e2017-11-22 16:36:07 -0500183 int32_t mode;
184 if (click->fMeta.findS32("mode", &mode)) {
Hal Canaryb1f411a2019-08-29 10:39:22 -0400185 if (fSelected && skui::InputState::kUp == click->fState) {
Brian Osmanede860e2017-11-22 16:36:07 -0500186 fSelected->fMode = gModes[mode];
187 }
188 } else {
189 fSelected->fLoc.fX += click->fCurr.fX - click->fPrev.fX;
190 fSelected->fLoc.fY += click->fCurr.fY - click->fPrev.fY;
191 }
reed1a9caff2015-09-02 19:05:10 -0700192 return true;
193 }
194
195private:
John Stiles7571f9e2020-09-02 22:42:33 -0400196 using INHERITED = Sample;
reed1a9caff2015-09-02 19:05:10 -0700197};
Mike Reedbd2b2c12020-07-24 15:30:49 -0400198DEF_SAMPLE( return new XferDemo; )
reed1a9caff2015-09-02 19:05:10 -0700199
200//////////////////////////////////////////////////////////////////////////////
201
Mike Reedbd2b2c12020-07-24 15:30:49 -0400202#include "tools/Resources.h"
203
204class CubicResamplerDemo : public Sample {
205 struct Rec {
206 sk_sp<SkImage> fImage;
207 SkRect fBounds;
208
Mike Reed15b95d62020-11-06 09:50:47 -0500209 void draw(SkCanvas* canvas, SkCubicResampler cubic) const {
Mike Reedbd2b2c12020-07-24 15:30:49 -0400210 SkRect r = fBounds;
211 SkPaint paint;
212
213 SkMatrix lm = SkMatrix::Translate(r.x(), r.y())
214 * SkMatrix::Scale(10, 10);
Mike Reed99c94462020-12-08 13:16:56 -0500215 paint.setShader(fImage->makeShader(SkSamplingOptions(), lm));
Mike Reedbd2b2c12020-07-24 15:30:49 -0400216 canvas->drawRect(r, paint);
217
218 r.offset(r.width() + 10, 0);
219 lm.postTranslate(r.width() + 10, 0);
220
Mike Reed5ec22382021-01-14 21:59:01 -0500221 paint.setShader(fImage->makeShader(SkSamplingOptions(SkFilterMode::kLinear), lm));
Mike Reedbd2b2c12020-07-24 15:30:49 -0400222 canvas->drawRect(r, paint);
223
224 r.offset(r.width() + 10, 0);
225 lm.postTranslate(r.width() + 10, 0);
226
Mike Reeda03f8bf2020-11-20 18:45:36 -0500227 paint.setShader(fImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
228 SkSamplingOptions(cubic), &lm));
Mike Reedbd2b2c12020-07-24 15:30:49 -0400229 canvas->drawRect(r, paint);
230 }
231 };
232 std::vector<Rec> fRecs;
233
234public:
235 CubicResamplerDemo() {
236 const char* names[] = {
237 "images/mandrill_128.png",
238 "images/rle.bmp",
239 "images/example_4.png",
240 };
241 SkRect r = {10, 10, 200, 200};
242 for (auto name : names) {
243 fRecs.push_back({GetResourceAsImage(name), r});
244 r.offset(0, r.height() + 10);
245 }
246
247 fDomain.setXYWH(r.fLeft + 3*r.width() + 40, 50, 200, 200);
248 fCubic = {.3f, .5f};
249 }
250
251protected:
252 SkString name() override { return SkString("CubicResampler"); }
253
254 void onDrawContent(SkCanvas* canvas) override {
255 for (const auto& rec : fRecs) {
256 rec.draw(canvas, fCubic);
257 }
258
259 SkPaint paint;
260 paint.setAntiAlias(true);
261 paint.setStroke(true);
262 canvas->drawRect(fDomain, paint);
263
264 paint.setColor(SK_ColorRED);
265 paint.setStroke(false);
Mike Reed2ac6ce82021-01-15 12:26:22 -0500266 SkPoint loc = SkMatrix::RectToRect({0,0,1,1}, fDomain).mapXY(fCubic.B, fCubic.C);
Mike Reedbd2b2c12020-07-24 15:30:49 -0400267 canvas->drawCircle(loc.fX, loc.fY, 8, paint);
268
269 SkString str;
270 str.printf("B=%4.2f C=%4.2f", fCubic.B, fCubic.C);
271 SkFont font;
272 font.setSize(25);
273 font.setEdging(SkFont::Edging::kAntiAlias);
274 paint.setColor(SK_ColorBLACK);
275 canvas->drawSimpleText(str.c_str(), str.size(), SkTextEncoding::kUTF8,
276 fDomain.fLeft + 10, fDomain.fBottom + 40, font, paint);
277 }
278
279 static float pin_unitize(float min, float max, float value) {
280 return (std::min(std::max(value, min), max) - min) / (max - min);
281 }
282 static SkPoint pin_unitize(const SkRect& r, SkPoint p) {
283 return {
284 pin_unitize(r.fLeft, r.fRight, p.fX),
285 pin_unitize(r.fTop, r.fBottom, p.fY),
286 };
287 }
288
289 Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) override {
290 if (fDomain.contains(x, y)) {
291 return new Click([this](Click* click) {
292 auto [B, C] = pin_unitize(fDomain, click->fCurr);
293 fCubic = {B, C};
294 return true;
295 });
296 }
297 return nullptr;
298 }
299
300private:
301 SkRect fDomain;
302 SkImage::CubicResampler fCubic;
303
John Stiles7571f9e2020-09-02 22:42:33 -0400304 using INHERITED = Sample;
Mike Reedbd2b2c12020-07-24 15:30:49 -0400305};
306DEF_SAMPLE( return new CubicResamplerDemo; )