Index: samplecode/SampleBevel.cpp |
diff --git a/samplecode/SampleBevel.cpp b/samplecode/SampleBevel.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..868ce35244288fa99b9c76c4ff0c75705650c173 |
--- /dev/null |
+++ b/samplecode/SampleBevel.cpp |
@@ -0,0 +1,306 @@ |
+/* |
+ * 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 BevelView : public SampleView { |
+public: |
+ BevelView() |
+ : fShapeBounds(SkRect::MakeWH(kShapeBoundsSize, kShapeBoundsSize)) |
+ , fRedLight(SkLights::Light::MakeDirectional(SkColor3f::Make(0.6f, 0.45f, 0.3f), |
+ SkVector3::Make(0.0f, -5.0f, 1.0f))) |
+ , fBlueLight(SkLights::Light::MakeDirectional(SkColor3f::Make(0.3f, 0.45f, 0.6f), |
+ SkVector3::Make(0.0f, 5.0f, 1.0f))) { |
+ this->setBGColor(0xFF666868); // Slightly colorized gray for contrast |
+ |
+ // Lights |
+ SkLights::Builder builder; |
+ builder.add(fRedLight); |
+ builder.add(fBlueLight); |
+ builder.add(SkLights::Light::MakeAmbient(SkColor3f::Make(0.4f, 0.4f, 0.4f))); |
+ fLights = builder.finish(); |
+ |
+ // Controls |
+ |
+ SkScalar currY = kSliderHeight; |
+ |
+ const SkScalar kWidthCtrlInitialPos = 0.2f; |
+ fCtrlRangeRects[0] = SkRect::MakeXYWH(0.0f, currY, |
+ kCtrlRange + kSliderWidth, |
+ kSliderHeight); |
+ fWidthCtrlRect = SkRect::MakeXYWH(kWidthCtrlInitialPos * kCtrlRange, currY, |
+ kSliderWidth, kSliderHeight); |
+ fBevelWidth = kBevelWidthMax * kWidthCtrlInitialPos; |
+ currY += 2 * kSliderHeight; |
+ |
+ const SkScalar kHeightCtrlInitialPos = 0.75f; |
+ fCtrlRangeRects[1] = SkRect::MakeXYWH(0.0f, currY, |
+ kCtrlRange + kSliderWidth, |
+ kSliderHeight); |
+ fHeightCtrlRect = SkRect::MakeXYWH(kHeightCtrlInitialPos * kCtrlRange, currY, |
+ kSliderWidth, kSliderHeight); |
+ // Mapping from (0, 1) to (-1, 1) |
+ fBevelHeight = kBevelHeightMax * (kHeightCtrlInitialPos * 2.0f - 1.0f); |
+ currY += 2 * kSliderHeight; |
+ |
+ const SkScalar kTypeCtrlInitialPos = 1.0f / (2.0f * kBevelTypeCount); |
+ fCtrlRangeRects[2] = SkRect::MakeXYWH(0.0f, currY, |
+ kCtrlRange + kSliderWidth, |
+ kSliderHeight); |
+ fTypeCtrlRect = SkRect::MakeXYWH(kTypeCtrlInitialPos * kCtrlRange, currY, |
+ kSliderWidth, kSliderHeight); |
+ fBevelType = (SkNormalSource::BevelType) SkScalarFloorToInt(kTypeCtrlInitialPos); |
+ currY += 2 * kSliderHeight; |
+ |
+ fSelectedCtrlRect = nullptr; |
+ 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(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 controls and labels |
+ |
+ // Draw controls |
+ |
+ SkPaint ctrlRectPaint; |
+ ctrlRectPaint.setColor(0xFFF3F3F3); |
+ canvas->drawRect(fWidthCtrlRect, ctrlRectPaint); |
+ canvas->drawRect(fHeightCtrlRect, ctrlRectPaint); |
+ canvas->drawRect(fTypeCtrlRect, ctrlRectPaint); |
+ |
+ SkPaint ctrlRectRangePaint; |
+ ctrlRectRangePaint.setColor(0xFFFFFFFF); |
+ ctrlRectRangePaint.setStyle(SkPaint::kStroke_Style); |
+ ctrlRectRangePaint.setStrokeWidth(2.0f); |
+ |
+ for (size_t i = 0; i < kNumControls; i++) { |
+ canvas->drawRect(fCtrlRangeRects[i], ctrlRectRangePaint); |
+ } |
+ |
+ // Draw labels |
+ constexpr SkScalar kTextSize = 12.0f; |
+ SkString widthLabel, heightLabel, typeLabel; |
+ SkPaint labelPaint; |
+ labelPaint.setTypeface(fLabelTypeface); |
+ labelPaint.setAntiAlias(true); |
+ labelPaint.setColor(0xFFFFFFFF); |
+ labelPaint.setTextSize(kTextSize); |
+ |
+ widthLabel.appendf("BevelWidth: %f", fBevelWidth); |
+ heightLabel.appendf("BevelHeight: %f", fBevelHeight); |
+ typeLabel.append("BevelType: "); |
+ |
+ switch (fBevelType) { |
+ case SkNormalSource::BevelType::kLinear: |
+ typeLabel.append("Linear"); |
+ break; |
+ case SkNormalSource::BevelType::kRoundedIn: |
+ typeLabel.append("RoundedIn"); |
+ break; |
+ case SkNormalSource::BevelType::kRoundedOut: |
+ typeLabel.append("RoundedOut"); |
+ break; |
+ } |
+ |
+ canvas->drawText(widthLabel.c_str(), widthLabel.size(), 0, |
+ fWidthCtrlRect.fTop - kTextSize/2.0f, labelPaint); |
+ canvas->drawText(heightLabel.c_str(), heightLabel.size(), 0, |
+ fHeightCtrlRect.fTop - kTextSize/2.0f, labelPaint); |
+ canvas->drawText(typeLabel.c_str(), typeLabel.size(), 0, |
+ fTypeCtrlRect.fTop - kTextSize/2.0f, labelPaint); |
+ |
+ canvas->restore(); // Return to modified matrix when drawing shapes |
+ |
+ // 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 { |
+ SkScalar x = click->fCurr.fX; |
+ SkScalar y = click->fCurr.fY; |
+ |
+ SkScalar dx = x - click->fPrev.fX; |
+ SkScalar dy = y - click->fPrev.fY; |
+ |
+ // Control deselection |
+ if (Click::State::kUp_State == click->fState) { |
+ fSelectedCtrlRect = nullptr; |
+ return true; |
+ } |
+ |
+ // Control selection |
+ if (nullptr == fSelectedCtrlRect && Click::State::kDown_State == click->fState) { |
+ if (fWidthCtrlRect.contains(SkRect::MakeXYWH(x, y, 1, 1))) { |
+ fSelectedCtrlRect = &fWidthCtrlRect; |
+ } else if (fHeightCtrlRect.contains(SkRect::MakeXYWH(x, y, 1, 1))) { |
+ fSelectedCtrlRect = &fHeightCtrlRect; |
+ } else if (fTypeCtrlRect.contains(SkRect::MakeXYWH(x, y, 1, 1))) { |
+ fSelectedCtrlRect = &fTypeCtrlRect; |
+ } |
+ } |
+ |
+ if (nullptr != fSelectedCtrlRect) { // Control modification |
+ fSelectedCtrlRect->offsetTo(SkScalarPin(x, 0.0f, kCtrlRange), fSelectedCtrlRect->fTop); |
+ |
+ fBevelHeight = (fHeightCtrlRect.fLeft / kCtrlRange) * kBevelHeightMax * 2.0f |
+ - kBevelHeightMax; |
+ fBevelWidth = (fWidthCtrlRect.fLeft / kCtrlRange) * kBevelWidthMax; |
+ fBevelType = (SkNormalSource::BevelType)SkTMin( |
+ SkScalarFloorToInt(kBevelTypeCount * fTypeCtrlRect.fLeft / kCtrlRange), |
+ kBevelTypeCount - 1); |
+ |
+ // Snap type controls to 3 positions |
+ fTypeCtrlRect.offsetTo(kCtrlRange * ( ((int)fBevelType)/SkIntToScalar(kBevelTypeCount) |
+ + 1.0f/(2.0f * kBevelTypeCount) ), |
+ fTypeCtrlRect.fTop); |
+ |
+ // Ensuring width is non-zero |
+ fBevelWidth = SkMaxScalar(1.0f, fBevelWidth); |
+ |
+ fDirtyNormalSource = true; |
+ |
+ this->inval(nullptr); |
+ return true; |
+ } else { // Moving light |
+ if (dx != 0 || dy != 0) { |
+ float recipX = 1.0f / kAppWidth; |
+ float recipY = 1.0f / kAppHeight; |
+ |
+ if (0 == click->fModifierKeys) { // No modifier |
+ fBlueLight = SkLights::Light::MakeDirectional(fBlueLight.color(), |
+ SkVector3::Make((kAppWidth/2.0f - x) * recipX * -3.0f, |
+ (kAppHeight/2.0f - y) * recipY * -3.0f, |
+ 1.0f)); |
+ } else if (1 == click->fModifierKeys) { // Shift key |
+ fRedLight = SkLights::Light::MakeDirectional(fRedLight.color(), |
+ SkVector3::Make((kAppWidth/2.0f - x) * recipX * -3.0f, |
+ (kAppHeight/2.0f - y) * recipY * -3.0f, |
+ 1.0f)); |
+ } |
+ |
+ SkLights::Builder builder; |
+ builder.add(fRedLight); |
+ builder.add(fBlueLight); |
+ builder.add(SkLights::Light::MakeAmbient( |
+ SkColor3f::Make(0.4f, 0.4f, 0.4f))); |
+ fLights = builder.finish(); |
+ |
+ this->inval(nullptr); |
+ } |
+ return true; |
+ } |
+ |
+ return true; |
+ } |
+ |
+private: |
+ static constexpr int kNumTestRects = 3; |
+ |
+ static constexpr SkScalar kAppWidth = 400.0f; |
+ static constexpr SkScalar kAppHeight = 400.0f; |
+ static constexpr SkScalar kShapeBoundsSize = 120.0f; |
+ |
+ static constexpr SkScalar kCtrlRange = 150.0f; |
+ static constexpr SkScalar kBevelWidthMax = kShapeBoundsSize; |
+ static constexpr SkScalar kBevelHeightMax = 50.0f; |
+ static constexpr int kBevelTypeCount = 3; |
+ |
+ static constexpr SkScalar kSliderHeight = 20.0f; |
+ static constexpr SkScalar kSliderWidth = 10.0f; |
+ |
+ const SkRect fShapeBounds; |
+ |
+ static constexpr int kNumControls = 3; |
+ SkRect fCtrlRangeRects[kNumControls]; |
+ SkRect* fSelectedCtrlRect; |
+ SkRect fWidthCtrlRect; |
+ SkRect fHeightCtrlRect; |
+ SkRect fTypeCtrlRect; |
+ |
+ SkScalar fBevelWidth; |
+ SkScalar fBevelHeight; |
+ SkNormalSource::BevelType fBevelType; |
+ sk_sp<SkNormalSource> fNormalSource; |
+ bool fDirtyNormalSource; |
+ |
+ sk_sp<SkLights> fLights; |
+ SkLights::Light fRedLight; |
+ SkLights::Light fBlueLight; |
+ |
+ sk_sp<SkTypeface> fLabelTypeface; |
+ |
+ typedef SampleView INHERITED; |
+}; |
+ |
+////////////////////////////////////////////////////////////////////////////// |
+ |
+static SkView* MyFactory() { return new BevelView; } |
+static SkViewRegister reg(MyFactory); |
+ |