• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2016 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 "SkCanvas.h"
10 #include "SkLightingShader.h"
11 #include "SkNormalSource.h"
12 #include "sk_tool_utils.h"
13 
14 class ParentControl;
15 
16 // Abstract base class for all components that a control panel must have
17 class Control : public SkRefCnt {
18 public:
Control(SkString name)19     Control(SkString name)
20         : fName(name)
21         , fParent(nullptr)
22         , fRelativePos(SkPoint::Make(0.0f, 0.0f)) {}
23 
24     // Use this to propagate a click's position down to a control. Gets modulated by the component's
25     // relative position
click(const SkPoint & clickPos)26     bool click(const SkPoint& clickPos) {
27         SkPoint relativeClickPos = SkPoint::Make(clickPos.fX - fRelativePos.fX,
28                                                  clickPos.fY - fRelativePos.fY);
29         return this->onClick(relativeClickPos);
30     }
31 
32     // Use this to draw the control and its appropriate children. Gets modulated by the component's
33     // relative position.
drawContent(SkCanvas * canvas)34     void drawContent(SkCanvas *canvas) {
35         canvas->save();
36         canvas->translate(fRelativePos.fX, fRelativePos.fY);
37         this->onDrawContent(canvas);
38         canvas->restore();
39     }
40 
41     /* Returns true when click position argumend lands over a control region in this control. Click
42      * position gets modulated by the component's relative position.
43      *
44      * @param click The position of the click in the coordinate space relative to the parent
45      */
isInCtrlRegion(const SkPoint & click)46     bool isInCtrlRegion(const SkPoint& click) {
47         SkPoint relativeClickPos = SkPoint::Make(click.fX - fRelativePos.fX,
48                                                  click.fY - fRelativePos.fY);
49         return this->onIsInCtrlRegion(relativeClickPos);
50     }
51 
52     // Returns height of content drawn
53     virtual SkScalar height() const = 0;
54 
55     // Sets the parent of this component. May only be used once. Height must remain constant after
56     // parent is set.
setParent(ParentControl * parent,const SkPoint & relativePos)57     void setParent(ParentControl *parent, const SkPoint& relativePos) {
58         SkASSERT(parent);
59         SkASSERT(!fParent); // No chidren transfer since relativeY would get invalid for younger kid
60 
61         fParent = parent;
62         fRelativePos = relativePos;
63         this->onSetParent();
64     }
65 
66     // Overriden by sub-classes that need to recompute fields after parent is set. Called after
67     // setting fParent.
onSetParent()68     virtual void onSetParent() {}
69 
70     // Overriden by sub-classes that need to know when a click is released.
onClickRelease()71     virtual void onClickRelease() {}
72 
73 protected:
74 
75     // Draws a label for the component, using its name and a passed value. Does NOT modulate by
76     // relative height, expects CTM to have been adjusted in advance.
drawLabel(SkCanvas * canvas,const SkString & valueStr) const77     void drawLabel(SkCanvas *canvas, const SkString& valueStr) const {
78         // TODO Cache this
79         sk_sp<SkTypeface> fLabelTypeface =
80                 sk_tool_utils::create_portable_typeface("sans-serif", SkFontStyle());
81 
82         SkString label;
83         label.append(fName);
84         label.append(": ");
85         label.append(valueStr);
86 
87         SkPaint labelPaint;
88         labelPaint.setTypeface(fLabelTypeface);
89         labelPaint.setAntiAlias(true);
90         labelPaint.setColor(0xFFFFFFFF);
91         labelPaint.setTextSize(12.0f);
92 
93         canvas->drawText(label.c_str(), label.size(), 0, kLabelHeight - 6.0f, labelPaint);
94     }
95 
96     SkString fName;
97     ParentControl* fParent;
98 
99     static constexpr SkScalar kLabelHeight = 20.0f;
100 
101 private:
102     // Overriden by sub-class to draw component. Do not call directly, drawContent() modulates by
103     // relative position.
104     virtual void onDrawContent(SkCanvas *canvas) = 0;
105 
106     // Overriden by sub-class to handle clicks. Do not call directly, click() modulates by relative
107     // position. Return true if holding mouse capture
onClick(const SkPoint & clickPos)108     virtual bool onClick(const SkPoint& clickPos) { return false; }
109 
110     // Overriden by sub-classes with controls. Should return true if clickPos lands inside a control
111     // region, to enable mouse caputre.
onIsInCtrlRegion(const SkPoint & clickPos) const112     virtual bool onIsInCtrlRegion(const SkPoint& clickPos) const { return false; }
113 
114     // The position of the control relative to it's parent
115     SkPoint fRelativePos;
116 };
117 
118 class ParentControl : public Control { // Interface for all controls that have children
119 public:
ParentControl(const SkString & name)120     ParentControl(const SkString& name) : INHERITED(name) {}
121 
122     // Adds a child
123     virtual void add(sk_sp<Control> control) = 0;
124 
125     // Returns the control's width. Used to propagate width down to components that don't specify it
126     virtual SkScalar width() const = 0;
127 
128 private:
129     typedef Control INHERITED;
130 };
131 
132 class ControlPanel : public ParentControl {
133 public:
134 
ControlPanel(SkScalar width)135     ControlPanel(SkScalar width)
136         : ParentControl(SkString("ControlPanel"))
137         , fWidth(width)
138         , fHeight(0.0f)
139         , fSelectedControl(-1) {}
140 
141     // Width unspecified, expectation is inheritance from parent
ControlPanel()142     ControlPanel() : ControlPanel(-1.0f) {}
143 
144     // Use this for introducing clicks on a ControlPanel from outside of the framework. It
145     // propagates click release or position down the chain. Returns false when click capture is
146     // being released.
inClick(SkView::Click * inClick)147     bool inClick(SkView::Click *inClick) {
148         if (SkView::Click::State::kUp_State == inClick->fState) {
149             this->onClickRelease();
150             return false;
151         }
152         return this->click(inClick->fCurr);
153     }
154 
155     // Add children
add(sk_sp<Control> control)156     void add(sk_sp<Control> control) override {
157         SkASSERT(!fParent); // Validity of parent's relativeY and fHeight depends on immutability
158         fControls.push_back(control);
159         control->setParent(this, SkPoint::Make(0.0f, fHeight));
160         fHeight += control->height();
161     }
162 
width() const163     SkScalar width() const override {
164         return fParent ? fParent->width() : fWidth; // Width inherited from parent if there is one
165     }
166 
height() const167     SkScalar height() const override {
168         return fHeight;
169     }
170 
171     // Propagate click release to selected control, deselect control
onClickRelease()172     void onClickRelease() override {
173         if (fSelectedControl >= 0) {
174             fControls[fSelectedControl]->onClickRelease();
175         }
176         fSelectedControl = -1;
177     }
178 
179     // Propagate onSetParent() down to children, some might need fParent->width() refresh
onSetParent()180     void onSetParent() override {
181         for (int i = 0; i < fControls.count(); i++) {
182             fControls[i]->onSetParent();
183         }
184     }
185 
186     // Holds a vertical shelf of controls. Can't be hierarchy root if not given a width value.
Make()187     static sk_sp<ParentControl> Make() {
188         return sk_sp<ParentControl>(new ControlPanel());
189     }
190 
191     // Holds a vertical shelf of controls. Only control that can be hooked from outside the
192     // framework.
Make(SkScalar width)193     static sk_sp<ParentControl> Make(SkScalar width) {
194         return sk_sp<ParentControl>(new ControlPanel(width));
195     }
196 
197 protected:
198     // Returns true if control panel has mouse captured, false when it is ready to release
199     // capture
onClick(const SkPoint & click)200     bool onClick(const SkPoint& click) override {
201 
202         if (fSelectedControl == -1) { // If no child control selected, check every child
203             for (int i = 0; i < fControls.count(); i++) {
204                 if (fControls[i]->isInCtrlRegion(click)) {
205                     fSelectedControl = i;
206                     break;
207                 }
208             }
209         }
210 
211         if (fSelectedControl >= 0) { // If child control selected, propagate click
212             bool keepSelection = fControls[fSelectedControl]->click(click);
213             if (!keepSelection) {
214                 fSelectedControl = -1;
215             }
216             return keepSelection;
217         }
218 
219         return false;
220     }
221 
222     // Draw all children
onDrawContent(SkCanvas * canvas)223     void onDrawContent(SkCanvas* canvas) override {
224         canvas->save();
225         for (int i = 0; i < fControls.count(); i++) {
226             fControls[i]->drawContent(canvas);
227         }
228         canvas->restore();
229     }
230 
231     // Check all children's control regions
onIsInCtrlRegion(const SkPoint & clickPos) const232     bool onIsInCtrlRegion(const SkPoint& clickPos) const override {
233         for (int i = 0; i < fControls.count(); i++) {
234             if (fControls[i]->isInCtrlRegion(clickPos)) {
235                 return true;
236             }
237         }
238 
239         return false;
240     }
241 
242 private:
243     SkScalar fWidth;
244     SkScalar fHeight;
245 
246     SkTArray<sk_sp<Control>> fControls;
247     int fSelectedControl;
248 };
249 
250 class DiscreteSliderControl : public Control {
251 public:
height() const252     SkScalar height() const override {
253         return 2.0f * kLabelHeight;
254     }
255 
256     // Set width-dependant variables when new parent is set
onSetParent()257     void onSetParent() override {
258         fCtrlRegion = SkRect::MakeXYWH(0.0f, kLabelHeight, fParent->width(), kSliderHeight);
259         fSliderRange = fParent->width() - kSliderWidth;
260     }
261 
262     /* Make a slider for an integer value. Snaps to discrete positions.
263      *
264      * @params name    The name of the control, displayed in the label
265      * @params output  Pointer to the integer that will be set by the slider
266      * @params min     Min value for output.
267      * @params max     Max value for output.
268      */
Make(SkString name,int * output,int min,int max)269     static sk_sp<Control> Make(SkString name, int* output, int min, int max) {
270         return sk_sp<Control>(new DiscreteSliderControl(name, output, min, max));
271     }
272 
273 protected:
onDrawContent(SkCanvas * canvas)274     void onDrawContent(SkCanvas* canvas) override {
275         SkASSERT(fParent);
276         int numChoices = fMax - fMin + 1;
277         fSlider.offsetTo(fSliderRange * ( (*fOutput)/SkIntToScalar(numChoices)
278                                           + 1.0f/(2.0f * numChoices) ),
279                          fSlider.fTop);
280 
281         SkString valueStr;
282         valueStr.appendS32(*fOutput);
283         this->drawLabel(canvas, valueStr);
284 
285         SkPaint sliderPaint;
286         sliderPaint.setColor(0xFFF3F3F3);
287         canvas->drawRect(fSlider, sliderPaint);
288 
289         SkPaint ctrlRegionPaint;
290         ctrlRegionPaint.setColor(0xFFFFFFFF);
291         ctrlRegionPaint.setStyle(SkPaint::kStroke_Style);
292         ctrlRegionPaint.setStrokeWidth(2.0f);
293         canvas->drawRect(fCtrlRegion, ctrlRegionPaint);
294     }
295 
onClick(const SkPoint & clickPos)296     bool onClick(const SkPoint& clickPos) override {
297         SkASSERT(fParent);
298         SkScalar x = SkScalarPin(clickPos.fX, 0.0f, fSliderRange);
299         int numChoices = fMax - fMin + 1;
300         *fOutput = SkTMin(SkScalarFloorToInt(numChoices * x / fSliderRange) + fMin, fMax);
301 
302         return true;
303     }
304 
onIsInCtrlRegion(const SkPoint & clickPos) const305     bool onIsInCtrlRegion(const SkPoint& clickPos) const override {
306         SkASSERT(fParent);
307         return fCtrlRegion.contains(SkRect::MakeXYWH(clickPos.fX, clickPos.fY, 1, 1));
308     }
309 
310 private:
DiscreteSliderControl(SkString name,int * output,int min,int max)311     DiscreteSliderControl(SkString name, int* output, int min, int max)
312             : INHERITED(name)
313             , fOutput(output)
314             , fMin(min)
315             , fMax(max) {
316         fSlider = SkRect::MakeXYWH(0, kLabelHeight, kSliderWidth, kSliderHeight);
317     }
318 
319     int* fOutput;
320     int fMin;
321     int fMax;
322     SkRect fSlider; // The rectangle that slides
323     // The region in which the rectangle slides. Also the region in which mouse is caputred
324     SkRect fCtrlRegion;
325     SkScalar fSliderRange; // The width in pixels over which the slider can slide
326 
327     static constexpr SkScalar kSliderHeight = 20.0f;
328     static constexpr SkScalar kSliderWidth = 10.0f;
329 
330     typedef Control INHERITED;
331 };
332 
333 class ControlSwitcher : public ParentControl {
334 public:
335     // Add children
add(sk_sp<Control> control)336     void add(sk_sp<Control> control) override {
337         SkASSERT(!fParent); // Validity of parent's relativeY and fHeight depends on immutability
338         fControls.push_back(control);
339         control->setParent(this, SkPoint::Make(0.0f, kSelectorHeight));
340         fHeight = SkMaxScalar(fHeight, control->height()); // Setting height to max child height.
341     }
342 
width() const343     SkScalar width() const override { return fParent ? (fParent->width()) : 0; }
344 
height() const345     SkScalar height() const override {
346         return fHeight;
347     }
348 
349     // Propagate onClickRelease to control that currently captures mouse
onClickRelease()350     void onClickRelease() override {
351         if (fCtrlOnClick) {
352             fCtrlOnClick->onClickRelease();
353         }
354         fCtrlOnClick = nullptr;
355     }
356 
onSetParent()357     void onSetParent() override {
358         for (int i = 0; i < fControls.count(); i++) {
359             fControls[i]->onSetParent(); // Propagate to children
360         }
361 
362         // Finalize control selector
363         // TODO can be moved to constructor if list-initialized
364         if (!finalizedChildren) {
365             fControlSelector = DiscreteSliderControl::Make(
366                     SkString(fName), &fSelectedControl, 0, fControls.count()-1);
367             fControlSelector->setParent(this, SkPoint::Make(0.0f, 0.0f));
368             fHeight += kSelectorHeight;
369 
370             SkASSERT(fControlSelector->height() <= kSelectorHeight);
371         }
372     }
373 
374     /* A set of a selector and a list of controls. Displays the control from the list of controls
375      * with the index set by the aforementioned selector.
376      *
377      * @param name The name of the switcher. Will be displayed in the selector's label.
378      */
Make(const SkString & name)379     static sk_sp<ParentControl> Make(const SkString& name) {
380         return sk_sp<ParentControl>(new ControlSwitcher(name));
381     }
382 
383 protected:
384     // Draw selector and currently selected control
onDrawContent(SkCanvas * canvas)385     void onDrawContent(SkCanvas* canvas) override {
386         fControlSelector->drawContent(canvas);
387         fControls[fSelectedControl]->drawContent(canvas);
388     }
389 
390     // Returns true if control panel has mouse captured, false when it is ready to release
391     // capture
onClick(const SkPoint & click)392     bool onClick(const SkPoint& click) override {
393         if (!fCtrlOnClick) {
394             if (fControlSelector->isInCtrlRegion(click)) {
395                 fCtrlOnClick = fControlSelector.get();
396             } else if (fControls[fSelectedControl]->isInCtrlRegion(click)) {
397                 fCtrlOnClick = fControls[fSelectedControl].get();
398             }
399         }
400         if (fCtrlOnClick) {
401             return fCtrlOnClick->click(click);
402         }
403 
404         return false;
405     }
406 
407     // Is in control region of selector or currently selected control
onIsInCtrlRegion(const SkPoint & clickPos) const408     bool onIsInCtrlRegion(const SkPoint& clickPos) const override {
409         if (fControlSelector->isInCtrlRegion(clickPos)) {
410             return true;
411         }
412         if (fControls[fSelectedControl]->isInCtrlRegion(clickPos)) {
413             return true;
414         }
415 
416         return false;
417     }
418 
419 private:
ControlSwitcher(const SkString & name)420     ControlSwitcher(const SkString& name)
421         : INHERITED(name)
422         , fHeight(0.0)
423         , fSelectedControl(0)
424         , fCtrlOnClick(nullptr){}
425 
426     bool finalizedChildren = false;
427 
428     sk_sp<Control> fControlSelector;
429     SkScalar fHeight;
430     SkTArray<sk_sp<Control>> fControls;
431     int fSelectedControl;
432 
433     Control* fCtrlOnClick;
434 
435     static constexpr SkScalar kSelectorHeight = 40.0f;
436 
437     typedef ParentControl INHERITED;
438 };
439 
440 class ContinuousSliderControl : public Control {
441 public:
height() const442     SkScalar height() const override {
443         return 2.0f * kLabelHeight;
444     }
445 
onSetParent()446     void onSetParent() override {
447         fSlider = SkRect::MakeXYWH(0, kLabelHeight, kSliderWidth, kSliderHeight);
448         fCtrlRegion = SkRect::MakeXYWH(0.0f, kLabelHeight, fParent->width(), kSliderHeight);
449         fSliderRange = fParent->width() - kSliderWidth;
450     }
451 
452     /* Make a slider for an SkScalar.
453      *
454      * @params name    The name of the control, displayed in the label
455      * @params output  Pointer to the SkScalar that will be set by the slider
456      * @params min     Min value for output
457      * @params max     Max value for output
458      */
Make(const SkString & name,SkScalar * output,SkScalar min,SkScalar max)459     static sk_sp<Control> Make(const SkString& name, SkScalar* output, SkScalar min, SkScalar max) {
460        return sk_sp<Control>(new ContinuousSliderControl(name, output, min, max));
461     }
462 
463 protected:
onDrawContent(SkCanvas * canvas)464     void onDrawContent(SkCanvas* canvas) override {
465         SkASSERT(fParent);
466         SkScalar x = fSliderRange * (*fOutput - fMin) / (fMax - fMin);
467         fSlider.offsetTo(SkScalarPin(x, 0.0f, fSliderRange), fSlider.fTop);
468 
469         SkString valueStr;
470         valueStr.appendScalar(*fOutput);
471         this->drawLabel(canvas, valueStr);
472 
473         SkPaint sliderPaint;
474         sliderPaint.setColor(0xFFF3F3F3);
475         canvas->drawRect(fSlider, sliderPaint);
476 
477         SkPaint ctrlRegionPaint;
478         ctrlRegionPaint.setColor(0xFFFFFFFF);
479         ctrlRegionPaint.setStyle(SkPaint::kStroke_Style);
480         ctrlRegionPaint.setStrokeWidth(2.0f);
481         canvas->drawRect(fCtrlRegion, ctrlRegionPaint);
482     }
483 
onClick(const SkPoint & clickPos)484     bool onClick(const SkPoint& clickPos) override {
485         SkASSERT(fParent);
486         SkScalar x = SkScalarPin(clickPos.fX, 0.0f, fSliderRange);
487         *fOutput = (x/fSliderRange) * (fMax - fMin) + fMin;
488         return true;
489     }
490 
onIsInCtrlRegion(const SkPoint & clickPos) const491     bool onIsInCtrlRegion(const SkPoint& clickPos) const override {
492         SkASSERT(fParent);
493         return fCtrlRegion.contains(SkRect::MakeXYWH(clickPos.fX, clickPos.fY, 1, 1));
494     }
495 
496 private:
ContinuousSliderControl(const SkString & name,SkScalar * output,SkScalar min,SkScalar max)497     ContinuousSliderControl(const SkString& name, SkScalar* output, SkScalar min, SkScalar max)
498             : INHERITED(name)
499             , fOutput(output)
500             , fMin(min)
501             , fMax(max) {}
502 
503     SkScalar* fOutput;
504     SkScalar fMin;
505     SkScalar fMax;
506     SkRect fSlider;
507     SkRect fCtrlRegion;
508     SkScalar fSliderRange;
509 
510     static constexpr SkScalar kSliderHeight = 20.0f;
511     static constexpr SkScalar kSliderWidth = 10.0f;
512 
513     typedef Control INHERITED;
514 };
515 
516 class RadialDirectionControl : public Control {
517 public:
height() const518     SkScalar height() const override {
519         return kLabelHeight + 2.0f * kRegionRadius;
520     }
521 
522     /* Make a direction selector.
523      *
524      * @params name    The name of the control, displayed in the label
525      * @params output  Pointer to the SkVector that will be set by the slider
526      */
Make(const SkString & name,SkVector * output)527     static sk_sp<Control> Make(const SkString& name, SkVector* output) {
528         return sk_sp<Control>(new RadialDirectionControl(name, output));
529     }
530 
531 protected:
onDrawContent(SkCanvas * canvas)532     void onDrawContent(SkCanvas* canvas) override {
533         SkASSERT(fParent);
534 
535         SkString valueStr;
536         valueStr.appendf("%.2f, %.2f", fOutput->fX, fOutput->fY);
537         this->drawLabel(canvas, valueStr);
538 
539         SkPoint lineEnd = SkPoint::Make(fCtrlRegion.centerX(), fCtrlRegion.centerY())
540                           + (*fOutput * (kRegionRadius - kCapRadius));
541         SkPaint linePaint;
542         linePaint.setColor(0xFFF3F3F3);
543         linePaint.setStrokeWidth(kStrokeWidth);
544         linePaint.setAntiAlias(true);
545         linePaint.setStrokeCap(SkPaint::kRound_Cap);
546         canvas->drawLine(fCtrlRegion.centerX(), fCtrlRegion.centerY(),
547                          lineEnd.fX, lineEnd.fY, linePaint);
548 
549         SkPaint ctrlRegionPaint;
550         ctrlRegionPaint.setColor(0xFFFFFFFF);
551         ctrlRegionPaint.setStyle(SkPaint::kStroke_Style);
552         ctrlRegionPaint.setStrokeWidth(2.0f);
553         ctrlRegionPaint.setAntiAlias(true);
554         canvas->drawCircle(fCtrlRegion.centerX(), fCtrlRegion.centerY(), kRegionRadius,
555                            ctrlRegionPaint);
556     }
557 
onClick(const SkPoint & clickPos)558     bool onClick(const SkPoint& clickPos) override {
559         SkASSERT(fParent);
560         fOutput->fX = clickPos.fX - fCtrlRegion.centerX();
561         fOutput->fY = clickPos.fY - fCtrlRegion.centerY();
562         fOutput->normalize();
563 
564         return true;
565     }
566 
onIsInCtrlRegion(const SkPoint & clickPos) const567     bool onIsInCtrlRegion(const SkPoint& clickPos) const override {
568         SkASSERT(fParent);
569         return fCtrlRegion.contains(SkRect::MakeXYWH(clickPos.fX, clickPos.fY,
570                                                      1, 1));
571     }
572 
573 private:
RadialDirectionControl(const SkString & name,SkVector * output)574     RadialDirectionControl(const SkString& name, SkVector* output)
575             : INHERITED(name)
576             , fOutput(output) {
577         fCtrlRegion = SkRect::MakeXYWH(0.0f, kLabelHeight,
578                                        kRegionRadius * 2.0f, kRegionRadius * 2.0f);
579     }
580 
581     SkVector* fOutput;
582     SkRect fCtrlRegion;
583 
584     static constexpr SkScalar kRegionRadius = 50.0f;
585     static constexpr SkScalar kStrokeWidth = 6.0f;
586     static constexpr SkScalar kCapRadius = kStrokeWidth / 2.0f;
587 
588     typedef Control INHERITED;
589 };
590 
591 class ColorDisplay: public Control {
592 public:
height() const593     SkScalar height() const override {
594         return kHeight;
595     }
596 
onSetParent()597     void onSetParent() override {
598         fDisplayRect = SkRect::MakeXYWH(0.0f, kPadding, fParent->width(), kHeight - kPadding);
599     }
600 
601     /* Make a display that shows an SkColor3f.
602      *
603      * @params output  Pointer to the SkColor3f that will be displayed
604      */
Make(SkColor3f * input)605     static sk_sp<Control> Make(SkColor3f* input) {
606         return sk_sp<Control>(new ColorDisplay(SkString("ColorDisplay"), input));
607     }
608 
609 protected:
onDrawContent(SkCanvas * canvas)610     void onDrawContent(SkCanvas* canvas) override {
611         SkASSERT(fParent);
612 
613         SkPaint displayPaint;
614         displayPaint.setColor(SkColor4f::FromColor3f(*fInput, 1.0f).toSkColor());
615         canvas->drawRect(fDisplayRect, displayPaint);
616     }
617 
618 private:
ColorDisplay(const SkString & name,SkColor3f * input)619     ColorDisplay(const SkString& name, SkColor3f* input)
620             : INHERITED(name)
621             , fInput(input) {}
622 
623     SkColor3f* fInput;
624     SkRect fDisplayRect;
625 
626     static constexpr SkScalar kHeight = 24.0f;
627     static constexpr SkScalar kPadding = 4.0f;
628 
629     typedef Control INHERITED;
630 };
631 
632 class BevelView : public SampleView {
633 public:
BevelView()634     BevelView()
635         : fShapeBounds(SkRect::MakeWH(kShapeBoundsSize, kShapeBoundsSize))
636         , fControlPanel(kCtrlRange) {
637         this->setBGColor(0xFF666868); // Slightly colorized gray for contrast
638 
639         // Controls
640         fBevelWidth = 25.0f;
641         fBevelHeight = 25.0f;
642         fBevelType = 0;
643 
644         int currLight = 0;
645         fLightDefs[currLight++] =
646                 {SkVector::Make(0.0f, 1.0f), 1.0f, SkColor3f::Make(0.6f, 0.45f, 0.3f)};
647         fLightDefs[currLight++] =
648                 {SkVector::Make(0.0f, -1.0f), 1.0f, SkColor3f::Make(0.3f, 0.45f, 0.6f)};
649         fLightDefs[currLight++] =
650                 {SkVector::Make(1.0f, 0.0f), 1.0f, SkColor3f::Make(0.0f, 0.0f, 0.0f)};
651         // Making sure we initialized all lights
652         SkASSERT(currLight == kNumLights);
653 
654         fControlPanel.add(ContinuousSliderControl::Make(SkString("BevelWidth"), &fBevelWidth,
655                                                         1.0f, kShapeBoundsSize));
656         fControlPanel.add(ContinuousSliderControl::Make(SkString("BevelHeight"), &fBevelHeight,
657                                                         -50.0f, 50.0f));
658         fControlPanel.add(DiscreteSliderControl::Make(SkString("BevelType"), &fBevelType,
659                                                       0, 2));
660         sk_sp<ParentControl> lightCtrlSelector = ControlSwitcher::Make(SkString("SelectedLight"));
661         for (int i = 0; i < kNumLights; i++) {
662             SkString name("Light");
663             name.appendS32(i);
664             sk_sp<ParentControl> currLightPanel = ControlPanel::Make();
665             SkString dirName(name);
666             dirName.append("Dir");
667             currLightPanel->add(RadialDirectionControl::Make(dirName, &(fLightDefs[i].fDirXY)));
668             SkString heightName(name);
669             heightName.append("Height");
670             currLightPanel->add(ContinuousSliderControl::Make(heightName, &(fLightDefs[i].fDirZ),
671                                                              0.0f, 2.0f));
672             SkString redName(name);
673             redName.append("Red");
674             currLightPanel->add(ContinuousSliderControl::Make(redName, &(fLightDefs[i].fColor.fX),
675                                                               0.0f, 1.0f));
676             SkString greenName(name);
677             greenName.append("Green");
678             currLightPanel->add(ContinuousSliderControl::Make(greenName, &(fLightDefs[i].fColor.fY),
679                                                               0.0f, 1.0f));
680             SkString blueName(name);
681             blueName.append("Blue");
682             currLightPanel->add(ContinuousSliderControl::Make(blueName, &(fLightDefs[i].fColor.fZ),
683                                                               0.0f, 1.0f));
684             currLightPanel->add(ColorDisplay::Make(&(fLightDefs[i].fColor)));
685             lightCtrlSelector->add(currLightPanel);
686         }
687         fControlPanel.add(lightCtrlSelector);
688 
689         fControlPanelSelected = false;
690         fDirtyNormalSource = true;
691 
692         fLabelTypeface = sk_tool_utils::create_portable_typeface("sans-serif", SkFontStyle());
693     }
694 
695 protected:
onQuery(SkEvent * evt)696     bool onQuery(SkEvent *evt) override {
697         if (SampleCode::TitleQ(*evt)) {
698             SampleCode::TitleR(evt, "Bevel");
699             return true;
700         }
701 
702         return this->INHERITED::onQuery(evt);
703     }
704 
705     enum Shape {
706         kCircle_Shape,
707         kRect_Shape,
708     };
drawShape(enum Shape shape,SkCanvas * canvas)709     void drawShape(enum Shape shape, SkCanvas* canvas) {
710         canvas->save();
711 
712         SkPaint paint;
713 
714         if (fDirtyNormalSource) {
715             fNormalSource = SkNormalSource::MakeBevel((SkNormalSource::BevelType)fBevelType,
716                                                       fBevelWidth, fBevelHeight);
717             fDirtyNormalSource = false;
718         }
719 
720         paint.setShader(SkLightingShader::Make(nullptr, fNormalSource, fLights));
721         paint.setAntiAlias(true);
722         paint.setColor(0xFFDDDDDD);
723         switch (shape) {
724             case kCircle_Shape:
725                 canvas->drawCircle(fShapeBounds.centerX(), fShapeBounds.centerY(),
726                                    fShapeBounds.width()/2.0f, paint);
727                 break;
728             case kRect_Shape:
729                 canvas->drawRect(fShapeBounds, paint);
730                 break;
731             default:
732                 SkDEBUGFAIL("Invalid shape enum for drawShape");
733         }
734 
735         canvas->restore();
736     }
737 
onDrawContent(SkCanvas * canvas)738     void onDrawContent(SkCanvas *canvas) override {
739 
740         canvas->save();
741         canvas->resetMatrix(); // Force static control panel position
742         fControlPanel.drawContent(canvas);
743         canvas->restore();
744 
745         SkLights::Builder builder;
746         for (int i = 0; i < kNumLights; i++) {
747             builder.add(SkLights::Light::MakeDirectional(fLightDefs[i].fColor,
748                                                          SkPoint3::Make(fLightDefs[i].fDirXY.fX,
749                                                                         fLightDefs[i].fDirXY.fY,
750                                                                         fLightDefs[i].fDirZ)));
751         }
752         builder.setAmbientLightColor(SkColor3f::Make(0.4f, 0.4f, 0.4f));
753         fLights = builder.finish();
754 
755         // Draw shapes
756         SkScalar xPos = kCtrlRange + 25.0f;
757         SkScalar yPos = fShapeBounds.height();
758         for (Shape shape : { kCircle_Shape, kRect_Shape }) {
759             canvas->save();
760             canvas->translate(xPos, yPos);
761             this->drawShape(shape, canvas);
762             canvas->restore();
763 
764             xPos += 1.2f * fShapeBounds.width();
765         }
766     }
767 
onFindClickHandler(SkScalar x,SkScalar y,unsigned modi)768     SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
769         return new SkView::Click(this);
770     }
771 
onClick(Click * click)772     bool onClick(Click *click) override {
773         // Control panel mouse handling
774         fControlPanelSelected = fControlPanel.inClick(click);
775 
776         if (fControlPanelSelected) { // Control modification
777             fDirtyNormalSource = true;
778 
779             this->inval(nullptr);
780             return true;
781         }
782 
783         // TODO move shapes
784         this->inval(nullptr);
785         return true;
786     }
787 
788 private:
789     static constexpr int kNumTestRects = 3;
790 
791     static constexpr SkScalar kShapeBoundsSize = 120.0f;
792 
793     static constexpr SkScalar kCtrlRange = 150.0f;
794 
795     static constexpr int kNumLights = 3;
796 
797     const SkRect fShapeBounds;
798 
799     SkScalar fBevelWidth;
800     SkScalar fBevelHeight;
801     int      fBevelType;
802 
803     sk_sp<SkNormalSource> fNormalSource;
804     bool fDirtyNormalSource;
805 
806     sk_sp<SkLights> fLights;
807 
808     struct LightDef {
809         SkVector fDirXY;
810         SkScalar fDirZ;
811         SkColor3f fColor;
812 
LightDefBevelView::LightDef813         LightDef() {}
LightDefBevelView::LightDef814         LightDef(SkVector dirXY, SkScalar dirZ, SkColor3f color)
815             : fDirXY(dirXY)
816             , fDirZ(dirZ)
817             , fColor(color) {}
818     };
819     LightDef fLightDefs[kNumLights];
820 
821     ControlPanel fControlPanel;
822     bool fControlPanelSelected;
823 
824     sk_sp<SkTypeface> fLabelTypeface;
825 
826     typedef SampleView INHERITED;
827 };
828 
829 //////////////////////////////////////////////////////////////////////////////
830 
MyFactory()831 static SkView* MyFactory() { return new BevelView; }
832 static SkViewRegister reg(MyFactory);
833 
834