| /* | |
| * Copyright 2016 Google Inc. | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "SampleCode.h" | |
| #include "SkCanvas.h" | |
| #include "SkLightingShader.h" | |
| #include "SkNormalSource.h" | |
| #include "sk_tool_utils.h" | |
| class ParentControl; | |
| // Abstract base class for all components that a control panel must have | |
| class Control : public SkRefCnt { | |
| public: | |
| Control(SkString name) | |
| : fName(name) | |
| , fParent(nullptr) | |
| , fRelativePos(SkPoint::Make(0.0f, 0.0f)) {} | |
| // Use this to propagate a click's position down to a control. Gets modulated by the component's | |
| // relative position | |
| bool click(const SkPoint& clickPos) { | |
| SkPoint relativeClickPos = SkPoint::Make(clickPos.fX - fRelativePos.fX, | |
| clickPos.fY - fRelativePos.fY); | |
| return this->onClick(relativeClickPos); | |
| } | |
| // Use this to draw the control and its appropriate children. Gets modulated by the component's | |
| // relative position. | |
| void drawContent(SkCanvas *canvas) { | |
| canvas->save(); | |
| canvas->translate(fRelativePos.fX, fRelativePos.fY); | |
| this->onDrawContent(canvas); | |
| canvas->restore(); | |
| } | |
| /* Returns true when click position argumend lands over a control region in this control. Click | |
| * position gets modulated by the component's relative position. | |
| * | |
| * @param click The position of the click in the coordinate space relative to the parent | |
| */ | |
| bool isInCtrlRegion(const SkPoint& click) { | |
| SkPoint relativeClickPos = SkPoint::Make(click.fX - fRelativePos.fX, | |
| click.fY - fRelativePos.fY); | |
| return this->onIsInCtrlRegion(relativeClickPos); | |
| } | |
| // Returns height of content drawn | |
| virtual SkScalar height() const = 0; | |
| // Sets the parent of this component. May only be used once. Height must remain constant after | |
| // parent is set. | |
| void setParent(ParentControl *parent, const SkPoint& relativePos) { | |
| SkASSERT(parent); | |
| SkASSERT(!fParent); // No chidren transfer since relativeY would get invalid for younger kid | |
| fParent = parent; | |
| fRelativePos = relativePos; | |
| this->onSetParent(); | |
| } | |
| // Overriden by sub-classes that need to recompute fields after parent is set. Called after | |
| // setting fParent. | |
| virtual void onSetParent() {} | |
| // Overriden by sub-classes that need to know when a click is released. | |
| virtual void onClickRelease() {} | |
| protected: | |
| // Draws a label for the component, using its name and a passed value. Does NOT modulate by | |
| // relative height, expects CTM to have been adjusted in advance. | |
| void drawLabel(SkCanvas *canvas, const SkString& valueStr) const { | |
| // TODO Cache this | |
| sk_sp<SkTypeface> fLabelTypeface = | |
| sk_tool_utils::create_portable_typeface("sans-serif", SkFontStyle()); | |
| SkString label; | |
| label.append(fName); | |
| label.append(": "); | |
| label.append(valueStr); | |
| SkPaint labelPaint; | |
| labelPaint.setTypeface(fLabelTypeface); | |
| labelPaint.setAntiAlias(true); | |
| labelPaint.setColor(0xFFFFFFFF); | |
| labelPaint.setTextSize(12.0f); | |
| canvas->drawText(label.c_str(), label.size(), 0, kLabelHeight - 6.0f, labelPaint); | |
| } | |
| SkString fName; | |
| ParentControl* fParent; | |
| static constexpr SkScalar kLabelHeight = 20.0f; | |
| private: | |
| // Overriden by sub-class to draw component. Do not call directly, drawContent() modulates by | |
| // relative position. | |
| virtual void onDrawContent(SkCanvas *canvas) = 0; | |
| // Overriden by sub-class to handle clicks. Do not call directly, click() modulates by relative | |
| // position. Return true if holding mouse capture | |
| virtual bool onClick(const SkPoint& clickPos) { return false; }; | |
| // Overriden by sub-classes with controls. Should return true if clickPos lands inside a control | |
| // region, to enable mouse caputre. | |
| virtual bool onIsInCtrlRegion(const SkPoint& clickPos) const { return false; }; | |
| // The position of the control relative to it's parent | |
| SkPoint fRelativePos; | |
| }; | |
| class ParentControl : public Control { // Interface for all controls that have children | |
| public: | |
| ParentControl(const SkString& name) : INHERITED(name) {} | |
| // Adds a child | |
| virtual void add(sk_sp<Control> control) = 0; | |
| // Returns the control's width. Used to propagate width down to components that don't specify it | |
| virtual SkScalar width() const = 0; | |
| private: | |
| typedef Control INHERITED; | |
| }; | |
| class ControlPanel : public ParentControl { | |
| public: | |
| ControlPanel(SkScalar width) | |
| : ParentControl(SkString("ControlPanel")) | |
| , fWidth(width) | |
| , fHeight(0.0f) | |
| , fSelectedControl(-1) {} | |
| // Width unspecified, expectation is inheritance from parent | |
| ControlPanel() : ControlPanel(-1.0f) {} | |
| // Use this for introducing clicks on a ControlPanel from outside of the framework. It | |
| // propagates click release or position down the chain. Returns false when click capture is | |
| // being released. | |
| bool inClick(SkView::Click *inClick) { | |
| if (SkView::Click::State::kUp_State == inClick->fState) { | |
| this->onClickRelease(); | |
| return false; | |
| } | |
| return this->click(inClick->fCurr); | |
| } | |
| // Add children | |
| void add(sk_sp<Control> control) override { | |
| SkASSERT(!fParent); // Validity of parent's relativeY and fHeight depends on immutability | |
| fControls.push_back(control); | |
| control->setParent(this, SkPoint::Make(0.0f, fHeight)); | |
| fHeight += control->height(); | |
| } | |
| SkScalar width() const override { | |
| return fParent ? fParent->width() : fWidth; // Width inherited from parent if there is one | |
| } | |
| SkScalar height() const override { | |
| return fHeight; | |
| } | |
| // Propagate click release to selected control, deselect control | |
| void onClickRelease() override { | |
| if (fSelectedControl >= 0) { | |
| fControls[fSelectedControl]->onClickRelease(); | |
| } | |
| fSelectedControl = -1; | |
| } | |
| // Propagate onSetParent() down to children, some might need fParent->width() refresh | |
| void onSetParent() override { | |
| for (int i = 0; i < fControls.count(); i++) { | |
| fControls[i]->onSetParent(); | |
| } | |
| } | |
| // Holds a vertical shelf of controls. Can't be hierarchy root if not given a width value. | |
| static sk_sp<ParentControl> Make() { | |
| return sk_sp<ParentControl>(new ControlPanel()); | |
| } | |
| // Holds a vertical shelf of controls. Only control that can be hooked from outside the | |
| // framework. | |
| static sk_sp<ParentControl> Make(SkScalar width) { | |
| return sk_sp<ParentControl>(new ControlPanel(width)); | |
| } | |
| protected: | |
| // Returns true if control panel has mouse captured, false when it is ready to release | |
| // capture | |
| bool onClick(const SkPoint& click) override { | |
| if (fSelectedControl == -1) { // If no child control selected, check every child | |
| for (int i = 0; i < fControls.count(); i++) { | |
| if (fControls[i]->isInCtrlRegion(click)) { | |
| fSelectedControl = i; | |
| break; | |
| } | |
| } | |
| } | |
| if (fSelectedControl >= 0) { // If child control selected, propagate click | |
| bool keepSelection = fControls[fSelectedControl]->click(click); | |
| if (!keepSelection) { | |
| fSelectedControl = -1; | |
| } | |
| return keepSelection; | |
| } | |
| return false; | |
| } | |
| // Draw all children | |
| void onDrawContent(SkCanvas* canvas) override { | |
| canvas->save(); | |
| for (int i = 0; i < fControls.count(); i++) { | |
| fControls[i]->drawContent(canvas); | |
| } | |
| canvas->restore(); | |
| } | |
| // Check all children's control regions | |
| bool onIsInCtrlRegion(const SkPoint& clickPos) const override { | |
| for (int i = 0; i < fControls.count(); i++) { | |
| if (fControls[i]->isInCtrlRegion(clickPos)) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| private: | |
| SkScalar fWidth; | |
| SkScalar fHeight; | |
| SkTArray<sk_sp<Control>> fControls; | |
| int fSelectedControl; | |
| }; | |
| class DiscreteSliderControl : public Control { | |
| public: | |
| SkScalar height() const override { | |
| return 2.0f * kLabelHeight; | |
| } | |
| // Set width-dependant variables when new parent is set | |
| void onSetParent() override { | |
| fCtrlRegion = SkRect::MakeXYWH(0.0f, kLabelHeight, fParent->width(), kSliderHeight); | |
| fSliderRange = fParent->width() - kSliderWidth; | |
| } | |
| /* Make a slider for an integer value. Snaps to discrete positions. | |
| * | |
| * @params name The name of the control, displayed in the label | |
| * @params output Pointer to the integer that will be set by the slider | |
| * @params min Min value for output. | |
| * @params max Max value for output. | |
| */ | |
| static sk_sp<Control> Make(SkString name, int* output, int min, int max) { | |
| return sk_sp<Control>(new DiscreteSliderControl(name, output, min, max)); | |
| } | |
| protected: | |
| void onDrawContent(SkCanvas* canvas) override { | |
| SkASSERT(fParent); | |
| int numChoices = fMax - fMin + 1; | |
| fSlider.offsetTo(fSliderRange * ( (*fOutput)/SkIntToScalar(numChoices) | |
| + 1.0f/(2.0f * numChoices) ), | |
| fSlider.fTop); | |
| SkString valueStr; | |
| valueStr.appendS32(*fOutput); | |
| this->drawLabel(canvas, valueStr); | |
| SkPaint sliderPaint; | |
| sliderPaint.setColor(0xFFF3F3F3); | |
| canvas->drawRect(fSlider, sliderPaint); | |
| SkPaint ctrlRegionPaint; | |
| ctrlRegionPaint.setColor(0xFFFFFFFF); | |
| ctrlRegionPaint.setStyle(SkPaint::kStroke_Style); | |
| ctrlRegionPaint.setStrokeWidth(2.0f); | |
| canvas->drawRect(fCtrlRegion, ctrlRegionPaint); | |
| } | |
| bool onClick(const SkPoint& clickPos) override { | |
| SkASSERT(fParent); | |
| SkScalar x = SkScalarPin(clickPos.fX, 0.0f, fSliderRange); | |
| int numChoices = fMax - fMin + 1; | |
| *fOutput = SkTMin(SkScalarFloorToInt(numChoices * x / fSliderRange) + fMin, fMax); | |
| return true; | |
| } | |
| bool onIsInCtrlRegion(const SkPoint& clickPos) const override { | |
| SkASSERT(fParent); | |
| return fCtrlRegion.contains(SkRect::MakeXYWH(clickPos.fX, clickPos.fY, 1, 1)); | |
| } | |
| private: | |
| DiscreteSliderControl(SkString name, int* output, int min, int max) | |
| : INHERITED(name) | |
| , fOutput(output) | |
| , fMin(min) | |
| , fMax(max) { | |
| fSlider = SkRect::MakeXYWH(0, kLabelHeight, kSliderWidth, kSliderHeight); | |
| } | |
| int* fOutput; | |
| int fMin; | |
| int fMax; | |
| SkRect fSlider; // The rectangle that slides | |
| // The region in which the rectangle slides. Also the region in which mouse is caputred | |
| SkRect fCtrlRegion; | |
| SkScalar fSliderRange; // The width in pixels over which the slider can slide | |
| static constexpr SkScalar kSliderHeight = 20.0f; | |
| static constexpr SkScalar kSliderWidth = 10.0f; | |
| typedef Control INHERITED; | |
| }; | |
| class ControlSwitcher : public ParentControl { | |
| public: | |
| // Add children | |
| void add(sk_sp<Control> control) override { | |
| SkASSERT(!fParent); // Validity of parent's relativeY and fHeight depends on immutability | |
| fControls.push_back(control); | |
| control->setParent(this, SkPoint::Make(0.0f, kSelectorHeight)); | |
| fHeight = SkMaxScalar(fHeight, control->height()); // Setting height to max child height. | |
| } | |
| SkScalar width() const override { return fParent ? (fParent->width()) : 0; } | |
| SkScalar height() const override { | |
| return fHeight; | |
| } | |
| // Propagate onClickRelease to control that currently captures mouse | |
| void onClickRelease() override { | |
| if (fCtrlOnClick) { | |
| fCtrlOnClick->onClickRelease(); | |
| } | |
| fCtrlOnClick = nullptr; | |
| } | |
| void onSetParent() override { | |
| for (int i = 0; i < fControls.count(); i++) { | |
| fControls[i]->onSetParent(); // Propagate to children | |
| } | |
| // Finalize control selector | |
| // TODO can be moved to constructor if list-initialized | |
| if (!finalizedChildren) { | |
| fControlSelector = DiscreteSliderControl::Make( | |
| SkString(fName), &fSelectedControl, 0, fControls.count()-1); | |
| fControlSelector->setParent(this, SkPoint::Make(0.0f, 0.0f)); | |
| fHeight += kSelectorHeight; | |
| SkASSERT(fControlSelector->height() <= kSelectorHeight); | |
| } | |
| } | |
| /* A set of a selector and a list of controls. Displays the control from the list of controls | |
| * with the index set by the aforementioned selector. | |
| * | |
| * @param name The name of the switcher. Will be displayed in the selector's label. | |
| */ | |
| static sk_sp<ParentControl> Make(const SkString& name) { | |
| return sk_sp<ParentControl>(new ControlSwitcher(name)); | |
| } | |
| protected: | |
| // Draw selector and currently selected control | |
| void onDrawContent(SkCanvas* canvas) override { | |
| fControlSelector->drawContent(canvas); | |
| fControls[fSelectedControl]->drawContent(canvas); | |
| } | |
| // Returns true if control panel has mouse captured, false when it is ready to release | |
| // capture | |
| bool onClick(const SkPoint& click) override { | |
| if (!fCtrlOnClick) { | |
| if (fControlSelector->isInCtrlRegion(click)) { | |
| fCtrlOnClick = fControlSelector.get(); | |
| } else if (fControls[fSelectedControl]->isInCtrlRegion(click)) { | |
| fCtrlOnClick = fControls[fSelectedControl].get(); | |
| } | |
| } | |
| if (fCtrlOnClick) { | |
| return fCtrlOnClick->click(click); | |
| } | |
| return false; | |
| } | |
| // Is in control region of selector or currently selected control | |
| bool onIsInCtrlRegion(const SkPoint& clickPos) const override { | |
| if (fControlSelector->isInCtrlRegion(clickPos)) { | |
| return true; | |
| } | |
| if (fControls[fSelectedControl]->isInCtrlRegion(clickPos)) { | |
| return true; | |
| } | |
| return false; | |
| } | |
| private: | |
| ControlSwitcher(const SkString& name) | |
| : INHERITED(name) | |
| , fHeight(0.0) | |
| , fSelectedControl(0) | |
| , fCtrlOnClick(nullptr){} | |
| bool finalizedChildren = false; | |
| sk_sp<Control> fControlSelector; | |
| SkScalar fHeight; | |
| SkTArray<sk_sp<Control>> fControls; | |
| int fSelectedControl; | |
| Control* fCtrlOnClick; | |
| static constexpr SkScalar kSelectorHeight = 40.0f; | |
| typedef ParentControl INHERITED; | |
| }; | |
| class ContinuousSliderControl : public Control { | |
| public: | |
| SkScalar height() const override { | |
| return 2.0f * kLabelHeight; | |
| } | |
| void onSetParent() override { | |
| fSlider = SkRect::MakeXYWH(0, kLabelHeight, kSliderWidth, kSliderHeight); | |
| fCtrlRegion = SkRect::MakeXYWH(0.0f, kLabelHeight, fParent->width(), kSliderHeight); | |
| fSliderRange = fParent->width() - kSliderWidth; | |
| } | |
| /* Make a slider for an SkScalar. | |
| * | |
| * @params name The name of the control, displayed in the label | |
| * @params output Pointer to the SkScalar that will be set by the slider | |
| * @params min Min value for output | |
| * @params max Max value for output | |
| */ | |
| static sk_sp<Control> Make(const SkString& name, SkScalar* output, SkScalar min, SkScalar max) { | |
| return sk_sp<Control>(new ContinuousSliderControl(name, output, min, max)); | |
| } | |
| protected: | |
| void onDrawContent(SkCanvas* canvas) override { | |
| SkASSERT(fParent); | |
| SkScalar x = fSliderRange * (*fOutput - fMin) / (fMax - fMin); | |
| fSlider.offsetTo(SkScalarPin(x, 0.0f, fSliderRange), fSlider.fTop); | |
| SkString valueStr; | |
| valueStr.appendScalar(*fOutput); | |
| this->drawLabel(canvas, valueStr); | |
| SkPaint sliderPaint; | |
| sliderPaint.setColor(0xFFF3F3F3); | |
| canvas->drawRect(fSlider, sliderPaint); | |
| SkPaint ctrlRegionPaint; | |
| ctrlRegionPaint.setColor(0xFFFFFFFF); | |
| ctrlRegionPaint.setStyle(SkPaint::kStroke_Style); | |
| ctrlRegionPaint.setStrokeWidth(2.0f); | |
| canvas->drawRect(fCtrlRegion, ctrlRegionPaint); | |
| } | |
| bool onClick(const SkPoint& clickPos) override { | |
| SkASSERT(fParent); | |
| SkScalar x = SkScalarPin(clickPos.fX, 0.0f, fSliderRange); | |
| *fOutput = (x/fSliderRange) * (fMax - fMin) + fMin; | |
| return true; | |
| } | |
| bool onIsInCtrlRegion(const SkPoint& clickPos) const override { | |
| SkASSERT(fParent); | |
| return fCtrlRegion.contains(SkRect::MakeXYWH(clickPos.fX, clickPos.fY, 1, 1)); | |
| } | |
| private: | |
| ContinuousSliderControl(const SkString& name, SkScalar* output, SkScalar min, SkScalar max) | |
| : INHERITED(name) | |
| , fOutput(output) | |
| , fMin(min) | |
| , fMax(max) {} | |
| SkScalar* fOutput; | |
| SkScalar fMin; | |
| SkScalar fMax; | |
| SkRect fSlider; | |
| SkRect fCtrlRegion; | |
| SkScalar fSliderRange; | |
| static constexpr SkScalar kSliderHeight = 20.0f; | |
| static constexpr SkScalar kSliderWidth = 10.0f; | |
| typedef Control INHERITED; | |
| }; | |
| class RadialDirectionControl : public Control { | |
| public: | |
| SkScalar height() const override { | |
| return kLabelHeight + 2.0f * kRegionRadius; | |
| } | |
| /* Make a direction selector. | |
| * | |
| * @params name The name of the control, displayed in the label | |
| * @params output Pointer to the SkVector that will be set by the slider | |
| */ | |
| static sk_sp<Control> Make(const SkString& name, SkVector* output) { | |
| return sk_sp<Control>(new RadialDirectionControl(name, output)); | |
| } | |
| protected: | |
| void onDrawContent(SkCanvas* canvas) override { | |
| SkASSERT(fParent); | |
| SkString valueStr; | |
| valueStr.appendf("%.2f, %.2f", fOutput->fX, fOutput->fY); | |
| this->drawLabel(canvas, valueStr); | |
| SkPoint lineEnd = SkPoint::Make(fCtrlRegion.centerX(), fCtrlRegion.centerY()) | |
| + (*fOutput * (kRegionRadius - kCapRadius)); | |
| SkPaint linePaint; | |
| linePaint.setColor(0xFFF3F3F3); | |
| linePaint.setStrokeWidth(kStrokeWidth); | |
| linePaint.setAntiAlias(true); | |
| linePaint.setStrokeCap(SkPaint::kRound_Cap); | |
| canvas->drawLine(fCtrlRegion.centerX(), fCtrlRegion.centerY(), | |
| lineEnd.fX, lineEnd.fY, linePaint); | |
| SkPaint ctrlRegionPaint; | |
| ctrlRegionPaint.setColor(0xFFFFFFFF); | |
| ctrlRegionPaint.setStyle(SkPaint::kStroke_Style); | |
| ctrlRegionPaint.setStrokeWidth(2.0f); | |
| ctrlRegionPaint.setAntiAlias(true); | |
| canvas->drawCircle(fCtrlRegion.centerX(), fCtrlRegion.centerY(), kRegionRadius, | |
| ctrlRegionPaint); | |
| } | |
| bool onClick(const SkPoint& clickPos) override { | |
| SkASSERT(fParent); | |
| fOutput->fX = clickPos.fX - fCtrlRegion.centerX(); | |
| fOutput->fY = clickPos.fY - fCtrlRegion.centerY(); | |
| fOutput->normalize(); | |
| return true; | |
| } | |
| bool onIsInCtrlRegion(const SkPoint& clickPos) const override { | |
| SkASSERT(fParent); | |
| return fCtrlRegion.contains(SkRect::MakeXYWH(clickPos.fX, clickPos.fY, | |
| 1, 1)); | |
| } | |
| private: | |
| RadialDirectionControl(const SkString& name, SkVector* output) | |
| : INHERITED(name) | |
| , fOutput(output) { | |
| fCtrlRegion = SkRect::MakeXYWH(0.0f, kLabelHeight, | |
| kRegionRadius * 2.0f, kRegionRadius * 2.0f); | |
| } | |
| SkVector* fOutput; | |
| SkRect fCtrlRegion; | |
| static constexpr SkScalar kRegionRadius = 50.0f; | |
| static constexpr SkScalar kStrokeWidth = 6.0f; | |
| static constexpr SkScalar kCapRadius = kStrokeWidth / 2.0f; | |
| typedef Control INHERITED; | |
| }; | |
| class ColorDisplay: public Control { | |
| public: | |
| SkScalar height() const override { | |
| return kHeight; | |
| } | |
| void onSetParent() override { | |
| fDisplayRect = SkRect::MakeXYWH(0.0f, kPadding, fParent->width(), kHeight - kPadding); | |
| } | |
| /* Make a display that shows an SkColor3f. | |
| * | |
| * @params output Pointer to the SkColor3f that will be displayed | |
| */ | |
| static sk_sp<Control> Make(SkColor3f* input) { | |
| return sk_sp<Control>(new ColorDisplay(SkString("ColorDisplay"), input)); | |
| } | |
| protected: | |
| void onDrawContent(SkCanvas* canvas) override { | |
| SkASSERT(fParent); | |
| SkPaint displayPaint; | |
| displayPaint.setColor(SkColor4f::FromColor3f(*fInput, 1.0f).toSkColor()); | |
| canvas->drawRect(fDisplayRect, displayPaint); | |
| } | |
| private: | |
| ColorDisplay(const SkString& name, SkColor3f* input) | |
| : INHERITED(name) | |
| , fInput(input) {} | |
| SkColor3f* fInput; | |
| SkRect fDisplayRect; | |
| static constexpr SkScalar kHeight = 24.0f; | |
| static constexpr SkScalar kPadding = 4.0f; | |
| typedef Control INHERITED; | |
| }; | |
| class BevelView : public SampleView { | |
| public: | |
| BevelView() | |
| : fShapeBounds(SkRect::MakeWH(kShapeBoundsSize, kShapeBoundsSize)) | |
| , fControlPanel(kCtrlRange) { | |
| this->setBGColor(0xFF666868); // Slightly colorized gray for contrast | |
| // Controls | |
| fBevelWidth = 25.0f; | |
| fBevelHeight = 25.0f; | |
| fBevelType = 0; | |
| int currLight = 0; | |
| fLightDefs[currLight++] = | |
| {SkVector::Make(0.0f, 1.0f), 1.0f, SkColor3f::Make(0.6f, 0.45f, 0.3f)}; | |
| fLightDefs[currLight++] = | |
| {SkVector::Make(0.0f, -1.0f), 1.0f, SkColor3f::Make(0.3f, 0.45f, 0.6f)}; | |
| fLightDefs[currLight++] = | |
| {SkVector::Make(1.0f, 0.0f), 1.0f, SkColor3f::Make(0.0f, 0.0f, 0.0f)}; | |
| // Making sure we initialized all lights | |
| SkASSERT(currLight == kNumLights); | |
| fControlPanel.add(ContinuousSliderControl::Make(SkString("BevelWidth"), &fBevelWidth, | |
| 1.0f, kShapeBoundsSize)); | |
| fControlPanel.add(ContinuousSliderControl::Make(SkString("BevelHeight"), &fBevelHeight, | |
| -50.0f, 50.0f)); | |
| fControlPanel.add(DiscreteSliderControl::Make(SkString("BevelType"), &fBevelType, | |
| 0, 2)); | |
| sk_sp<ParentControl> lightCtrlSelector = ControlSwitcher::Make(SkString("SelectedLight")); | |
| for (int i = 0; i < kNumLights; i++) { | |
| SkString name("Light"); | |
| name.appendS32(i); | |
| sk_sp<ParentControl> currLightPanel = ControlPanel::Make(); | |
| SkString dirName(name); | |
| dirName.append("Dir"); | |
| currLightPanel->add(RadialDirectionControl::Make(dirName, &(fLightDefs[i].fDirXY))); | |
| SkString heightName(name); | |
| heightName.append("Height"); | |
| currLightPanel->add(ContinuousSliderControl::Make(heightName, &(fLightDefs[i].fDirZ), | |
| 0.0f, 2.0f)); | |
| SkString redName(name); | |
| redName.append("Red"); | |
| currLightPanel->add(ContinuousSliderControl::Make(redName, &(fLightDefs[i].fColor.fX), | |
| 0.0f, 1.0f)); | |
| SkString greenName(name); | |
| greenName.append("Green"); | |
| currLightPanel->add(ContinuousSliderControl::Make(greenName, &(fLightDefs[i].fColor.fY), | |
| 0.0f, 1.0f)); | |
| SkString blueName(name); | |
| blueName.append("Blue"); | |
| currLightPanel->add(ContinuousSliderControl::Make(blueName, &(fLightDefs[i].fColor.fZ), | |
| 0.0f, 1.0f)); | |
| currLightPanel->add(ColorDisplay::Make(&(fLightDefs[i].fColor))); | |
| lightCtrlSelector->add(currLightPanel); | |
| } | |
| fControlPanel.add(lightCtrlSelector); | |
| fControlPanelSelected = false; | |
| fDirtyNormalSource = true; | |
| fLabelTypeface = sk_tool_utils::create_portable_typeface("sans-serif", SkFontStyle()); | |
| } | |
| protected: | |
| bool onQuery(SkEvent *evt) override { | |
| if (SampleCode::TitleQ(*evt)) { | |
| SampleCode::TitleR(evt, "Bevel"); | |
| return true; | |
| } | |
| return this->INHERITED::onQuery(evt); | |
| } | |
| enum Shape { | |
| kCircle_Shape, | |
| kRect_Shape, | |
| }; | |
| void drawShape(enum Shape shape, SkCanvas* canvas) { | |
| canvas->save(); | |
| SkPaint paint; | |
| if (fDirtyNormalSource) { | |
| fNormalSource = SkNormalSource::MakeBevel((SkNormalSource::BevelType)fBevelType, | |
| fBevelWidth, fBevelHeight); | |
| fDirtyNormalSource = false; | |
| } | |
| paint.setShader(SkLightingShader::Make(nullptr, fNormalSource, fLights)); | |
| paint.setAntiAlias(true); | |
| paint.setColor(0xFFDDDDDD); | |
| switch (shape) { | |
| case kCircle_Shape: | |
| canvas->drawCircle(fShapeBounds.centerX(), fShapeBounds.centerY(), | |
| fShapeBounds.width()/2.0f, paint); | |
| break; | |
| case kRect_Shape: | |
| canvas->drawRect(fShapeBounds, paint); | |
| break; | |
| default: | |
| SkDEBUGFAIL("Invalid shape enum for drawShape"); | |
| } | |
| canvas->restore(); | |
| } | |
| void onDrawContent(SkCanvas *canvas) override { | |
| canvas->save(); | |
| canvas->resetMatrix(); // Force static control panel position | |
| fControlPanel.drawContent(canvas); | |
| canvas->restore(); | |
| SkLights::Builder builder; | |
| for (int i = 0; i < kNumLights; i++) { | |
| builder.add(SkLights::Light::MakeDirectional(fLightDefs[i].fColor, | |
| SkPoint3::Make(fLightDefs[i].fDirXY.fX, | |
| fLightDefs[i].fDirXY.fY, | |
| fLightDefs[i].fDirZ))); | |
| } | |
| builder.setAmbientLightColor(SkColor3f::Make(0.4f, 0.4f, 0.4f)); | |
| fLights = builder.finish(); | |
| // Draw shapes | |
| SkScalar xPos = kCtrlRange + 25.0f; | |
| SkScalar yPos = fShapeBounds.height(); | |
| for (Shape shape : { kCircle_Shape, kRect_Shape }) { | |
| canvas->save(); | |
| canvas->translate(xPos, yPos); | |
| this->drawShape(shape, canvas); | |
| canvas->restore(); | |
| xPos += 1.2f * fShapeBounds.width(); | |
| } | |
| } | |
| SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override { | |
| return new SkView::Click(this); | |
| } | |
| bool onClick(Click *click) override { | |
| // Control panel mouse handling | |
| fControlPanelSelected = fControlPanel.inClick(click); | |
| if (fControlPanelSelected) { // Control modification | |
| fDirtyNormalSource = true; | |
| this->inval(nullptr); | |
| return true; | |
| } | |
| // TODO move shapes | |
| this->inval(nullptr); | |
| return true; | |
| } | |
| private: | |
| static constexpr int kNumTestRects = 3; | |
| static constexpr SkScalar kShapeBoundsSize = 120.0f; | |
| static constexpr SkScalar kCtrlRange = 150.0f; | |
| static constexpr int kNumLights = 3; | |
| const SkRect fShapeBounds; | |
| SkScalar fBevelWidth; | |
| SkScalar fBevelHeight; | |
| int fBevelType; | |
| sk_sp<SkNormalSource> fNormalSource; | |
| bool fDirtyNormalSource; | |
| sk_sp<SkLights> fLights; | |
| struct LightDef { | |
| SkVector fDirXY; | |
| SkScalar fDirZ; | |
| SkColor3f fColor; | |
| LightDef() {} | |
| LightDef(SkVector dirXY, SkScalar dirZ, SkColor3f color) | |
| : fDirXY(dirXY) | |
| , fDirZ(dirZ) | |
| , fColor(color) {} | |
| }; | |
| LightDef fLightDefs[kNumLights]; | |
| ControlPanel fControlPanel; | |
| bool fControlPanelSelected; | |
| sk_sp<SkTypeface> fLabelTypeface; | |
| typedef SampleView INHERITED; | |
| }; | |
| ////////////////////////////////////////////////////////////////////////////// | |
| static SkView* MyFactory() { return new BevelView; } | |
| static SkViewRegister reg(MyFactory); | |