1/*
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
8#include "SampleCode.h"
9#include "SkAnimTimer.h"
10#include "SkDrawable.h"
11#include "SkView.h"
12#include "SkCanvas.h"
13#include "SkDrawable.h"
14#include "SkPath.h"
15#include "SkRandom.h"
16#include "SkRSXform.h"
17#include "SkSurface.h"
18#include "SkGradientShader.h"
19
20const struct {
21    SkXfermode::Mode fMode;
22    const char*      fName;
23} gModes[] = {
24    { SkXfermode::kSrcOver_Mode, "src-over" },
25    { SkXfermode::kSrc_Mode,     "src" },
26    { SkXfermode::kSrcIn_Mode,   "src-in" },
27    { SkXfermode::kSrcOut_Mode,  "src-out" },
28    { SkXfermode::kSrcATop_Mode, "src-atop" },
29    { SkXfermode::kDstOver_Mode, "dst-over" },
30    { SkXfermode::kDstIn_Mode,   "dst-in" },
31    { SkXfermode::kDstOut_Mode,  "dst-out" },
32    { SkXfermode::kDstATop_Mode, "dst-atop" },
33};
34const int N_Modes = SK_ARRAY_COUNT(gModes);
35
36class HasEventWig : public SkView {
37public:
38    void postWidgetEvent() {
39        SkEvent evt;
40        this->onPrepareWidEvent(&evt);
41        this->postToListeners(evt, 0);
42    }
43
44protected:
45    virtual void onPrepareWidEvent(SkEvent*) {}
46};
47
48static SkRandom gRand;
49
50class PushButtonWig : public HasEventWig {
51    SkString fLabel;
52    SkColor  fColor;
53    uint32_t fFast32;
54
55public:
56    PushButtonWig(const char label[], uint32_t fast) : fLabel(label) {
57        fColor = (gRand.nextU() & 0x7F7F7F7F) | SkColorSetARGB(0xFF, 0, 0, 0x80);
58        fFast32 = fast;
59    }
60
61protected:
62    void onPrepareWidEvent(SkEvent* evt) override {
63        evt->setType("push-button");
64        evt->setFast32(fFast32);
65        evt->setString("label", fLabel.c_str());
66    }
67
68//    bool onEvent(const SkEvent&) override;
69    void onDraw(SkCanvas* canvas) override {
70        SkRect r;
71        this->getLocalBounds(&r);
72        SkPaint paint;
73        paint.setAntiAlias(true);
74        paint.setColor(fColor);
75        canvas->drawRoundRect(r, 8, 8, paint);
76
77        paint.setColor(0xFFFFFFFF);
78        paint.setTextSize(16);
79        paint.setTextAlign(SkPaint::kCenter_Align);
80        paint.setLCDRenderText(true);
81        canvas->drawText(fLabel.c_str(), fLabel.size(), r.centerX(), r.fTop + 0.68f * r.height(), paint);
82    }
83
84    Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
85        return new Click(this);
86    }
87
88    bool onClick(Click* click) override {
89        SkRect target = SkRect::MakeXYWH(click->fCurr.x() - 1, click->fCurr.y() - 1, 3, 3);
90        SkRect r;
91        this->getLocalBounds(&r);
92        if (r.intersects(target)) {
93            fColor = SkColorSetA(fColor, 0x99);
94        } else {
95            fColor = SkColorSetA(fColor, 0xFF);
96        }
97        this->inval(nullptr);
98
99        if (click->fState == SkView::Click::kUp_State) {
100            this->postWidgetEvent();
101        }
102        return true;
103    }
104
105private:
106    typedef HasEventWig INHERITED;
107};
108
109
110class ModeDrawable : public SkDrawable {
111public:
112    ModeDrawable() : fMode(SkXfermode::kSrcOver_Mode), fLoc(SkPoint::Make(0, 0)) {}
113
114    SkXfermode::Mode fMode;
115    SkPoint          fLoc;
116
117    bool hitTest(SkScalar x, SkScalar y) {
118        SkRect target = SkRect::MakeXYWH(x - fLoc.x() - 1, y - fLoc.y() - 1, 3, 3);
119        return this->getBounds().intersects(target);
120    }
121};
122
123class CircDrawable : public ModeDrawable {
124    SkPaint fPaint;
125    SkRect  fBounds;
126
127public:
128    CircDrawable(SkScalar size, SkColor c) {
129        const SkColor colors[] = { 0, c };
130        SkAutoTUnref<SkShader> shader(SkGradientShader::CreateRadial(SkPoint::Make(size/2, size/2), size/2,
131                                                                     colors, nullptr, 2,
132                                                                     SkShader::kClamp_TileMode));
133        fPaint.setShader(shader);
134        fBounds = SkRect::MakeWH(size, size);
135    }
136
137protected:
138    SkRect onGetBounds() override {
139        return fBounds;
140    }
141
142    void onDraw(SkCanvas* canvas) override {
143        fPaint.setXfermodeMode(fMode);
144        canvas->save();
145        canvas->translate(fLoc.x(), fLoc.y());
146        canvas->drawOval(fBounds, fPaint);
147        canvas->restore();
148    }
149};
150
151class XferDemo : public SampleView {
152    enum {
153        N = 4
154    };
155
156    SkRect        fModeRect[N_Modes];
157    SkAutoTUnref<CircDrawable> fDrs[N];
158    CircDrawable* fSelected;
159
160    void addButtons() {
161        SkScalar x = 10;
162        SkScalar y = 10;
163        for (int i = 0; i < N_Modes; ++i) {
164            SkAutoTUnref<SkView> v(new PushButtonWig(gModes[i].fName, gModes[i].fMode));
165            v->setSize(70, 25);
166            v->setLoc(x, y);
167            v->setVisibleP(true);
168            v->setEnabledP(true);
169            v->addListenerID(this->getSinkID());
170            this->attachChildToFront(v);
171            fModeRect[i] = SkRect::MakeXYWH(x, y + 28, 70, 2);
172            x += 80;
173        }
174    }
175
176public:
177    XferDemo() {
178        const SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorBLACK };
179        for (int i = 0; i < N; ++i) {
180            fDrs[i].reset(new CircDrawable(200, colors[i]));
181            fDrs[i]->fLoc.set(100.f + i * 100, 100.f + i * 100);
182            fDrs[i]->fMode = SkXfermode::kSrcOver_Mode;
183        }
184        fSelected = nullptr;
185
186        this->addButtons();
187    }
188
189protected:
190    bool onEvent(const SkEvent& evt) override {
191        if (evt.isType("push-button")) {
192            if (fSelected) {
193                fSelected->fMode = (SkXfermode::Mode)evt.getFast32();
194                this->inval(nullptr);
195            }
196            return true;
197        }
198        return this->INHERITED::onEvent(evt);
199    }
200
201    bool onQuery(SkEvent* evt) override {
202        if (SampleCode::TitleQ(*evt)) {
203            SampleCode::TitleR(evt, "XferDemo");
204            return true;
205        }
206        return this->INHERITED::onQuery(evt);
207    }
208
209    void onDrawContent(SkCanvas* canvas) override {
210        SkPaint paint;
211        if (fSelected) {
212            for (int i = 0; i < N_Modes; ++i) {
213                if (fSelected->fMode == gModes[i].fMode) {
214                    canvas->drawRect(fModeRect[i], paint);
215                    break;
216                }
217            }
218        }
219
220        canvas->saveLayer(nullptr, nullptr);
221        for (int i = 0; i < N; ++i) {
222            fDrs[i]->draw(canvas);
223        }
224        canvas->restore();
225    }
226
227    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned) override {
228        fSelected = nullptr;
229        for (int i = N - 1; i >= 0; --i) {
230            if (fDrs[i]->hitTest(x, y)) {
231                fSelected = fDrs[i];
232                break;
233            }
234        }
235        this->inval(nullptr);
236        return fSelected ? new Click(this) : nullptr;
237    }
238
239    bool onClick(Click* click) override {
240        fSelected->fLoc.fX += click->fCurr.fX - click->fPrev.fX;
241        fSelected->fLoc.fY += click->fCurr.fY - click->fPrev.fY;
242        this->inval(nullptr);
243        return true;
244    }
245
246private:
247    typedef SampleView INHERITED;
248};
249
250//////////////////////////////////////////////////////////////////////////////
251
252DEF_SAMPLE( return new XferDemo; )
253