| /* |
| * 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 "SkPictureRecorder.h" |
| #include "SkShadowPaintFilterCanvas.h" |
| #include "SkShadowShader.h" |
| #include "SkSurface.h" |
| |
| #ifdef SK_EXPERIMENTAL_SHADOWING |
| |
| class ShadowingView : public SampleView { |
| public: |
| ShadowingView() |
| : fSceneChanged(true) |
| , fLightsChanged(true) |
| , fMoveLight(false) |
| , fClearShadowMaps(false) |
| , fSelectedRectID(-1) |
| , fSelectedSliderID(-1) |
| , fLightDepth(400.0f) { |
| this->setBGColor(0xFFCCCCCC); |
| |
| this->updateLights(100, 100); |
| |
| fTestRects[0].fColor = 0xFFEE8888; |
| fTestRects[0].fDepth = 80; |
| fTestRects[0].fGeometry = SkRect::MakeLTRB(300,200,350,250); |
| |
| fTestRects[1].fColor = 0xFF88EE88; |
| fTestRects[1].fDepth = 160; |
| fTestRects[1].fGeometry = SkRect::MakeLTRB(200,300,250,350); |
| |
| fTestRects[2].fColor = 0xFF8888EE; |
| fTestRects[2].fDepth = 240; |
| fTestRects[2].fGeometry = SkRect::MakeLTRB(100,100,150,150); |
| |
| fSliders[0].fGeometry = SkRect::MakeLTRB(20, 400, 30, 420); |
| fSliders[0].fOffset = 0.0f; |
| fSliders[0].fScale = 0.1f; |
| |
| fSliders[1].fGeometry = SkRect::MakeLTRB(100, 420, 110, 440); |
| fSliders[1].fOffset = 0.0f; |
| fSliders[1].fScale = 10.0f; |
| |
| fSliders[2].fGeometry = SkRect::MakeLTRB(0, 440, 10, 460); |
| fSliders[2].fOffset = 0.0f; |
| fSliders[2].fScale = 0.0025f; |
| |
| fShadowParams.fShadowRadius = 4.0f; |
| fShadowParams.fBiasingConstant = 0.3f; |
| fShadowParams.fMinVariance = 2048; // we need a higher min variance for point lights |
| fShadowParams.fType = SkShadowParams::kNoBlur_ShadowType; |
| } |
| |
| protected: |
| bool onQuery(SkEvent *evt) override { |
| if (SampleCode::TitleQ(*evt)) { |
| SampleCode::TitleR(evt, "shadowing"); |
| return true; |
| } |
| |
| SkUnichar uni; |
| if (SampleCode::CharQ(*evt, &uni)) { |
| switch (uni) { |
| case 'L': |
| fMoveLight = !fMoveLight; |
| break; |
| case 'd': |
| // Raster generated shadow maps have their origin in the UL corner |
| // GPU shadow maps can have an arbitrary origin. |
| // We override the 'd' keypress so that when the device is cycled, |
| // the shadow maps will be re-generated according to the new backend. |
| fClearShadowMaps = true; |
| break; |
| case 'q': |
| fLightDepth += 5.0f; |
| fMoveLight = true; |
| break; |
| case 'B': |
| if (SkShadowParams::kVariance_ShadowType == fShadowParams.fType) { |
| fShadowParams.fType = SkShadowParams::kNoBlur_ShadowType; |
| } else if (SkShadowParams::kNoBlur_ShadowType == |
| fShadowParams.fType) { |
| fShadowParams.fType = SkShadowParams::kVariance_ShadowType; |
| } |
| fLightsChanged = true; |
| break; |
| case 'w': |
| fLightDepth -= 5.0f; |
| fMoveLight = true; |
| break; |
| default: |
| break; |
| } |
| } |
| return this->INHERITED::onQuery(evt); |
| } |
| |
| sk_sp<SkPicture> makeTestPicture(int width, int height) { |
| SkPictureRecorder recorder; |
| |
| // LONG RANGE TODO: eventually add SkBBHFactory (bounding box factory) |
| SkCanvas* canvas = recorder.beginRecording(SkRect::MakeIWH(width, height)); |
| |
| SkASSERT(canvas->getTotalMatrix().isIdentity()); |
| SkPaint paint; |
| paint.setColor(SK_ColorGRAY); |
| |
| // LONG RANGE TODO: tag occluders |
| // LONG RANGE TODO: track number of IDs we need (hopefully less than 256) |
| // and determinate the mapping from z to id |
| |
| // universal receiver, "ground" |
| canvas->drawRect(SkRect::MakeIWH(width, height), paint); |
| |
| for (int i = 0; i < kNumTestRects; i++) { |
| paint.setColor(fTestRects[i].fColor); |
| if (i == 0) { |
| canvas->translateZ(fTestRects[0].fDepth); |
| } else { |
| canvas->translateZ(fTestRects[i].fDepth - fTestRects[i-1].fDepth); |
| } |
| canvas->drawRect(fTestRects[i].fGeometry, paint); |
| } |
| |
| return recorder.finishRecordingAsPicture(); |
| } |
| |
| void onDrawContent(SkCanvas *canvas) override { |
| if (fSceneChanged) { |
| fPicture = this->makeTestPicture(kWidth, kHeight); |
| } |
| |
| if (fSceneChanged || fLightsChanged || fClearShadowMaps) { |
| for (int i = 0; i < fLights->numLights(); i++) { |
| fLights->light(i).setShadowMap(nullptr); |
| } |
| |
| fSceneChanged = false; |
| fLightsChanged = false; |
| fClearShadowMaps = false; |
| } |
| |
| canvas->setLights(fLights); |
| canvas->drawShadowedPicture(fPicture, nullptr, nullptr, fShadowParams); |
| |
| for (int i = 0; i < kNumSliders; i++) { |
| SkPaint paint; |
| paint.setColor(SK_ColorBLACK); |
| canvas->drawRect(fSliders[i].fGeometry, paint); |
| } |
| } |
| |
| SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override { |
| return new SkView::Click(this); |
| } |
| |
| void updateLights(int x, int y) { |
| SkLights::Builder builder; |
| builder.add(SkLights::Light::MakePoint(SkColor3f::Make(1.0f, 1.0f, 1.0f), |
| SkVector3::Make(x, |
| kHeight - y, |
| fLightDepth), |
| 400, true)); |
| fLights = builder.finish(); |
| } |
| |
| void updateFromSelectedSlider() { |
| SkScalar newValue = fSliders[fSelectedSliderID].fGeometry.fLeft * |
| fSliders[fSelectedSliderID].fScale + |
| fSliders[fSelectedSliderID].fOffset; |
| |
| switch (fSelectedSliderID) { |
| case 0: |
| fShadowParams.fShadowRadius = newValue; |
| break; |
| case 1: |
| fShadowParams.fMinVariance = newValue; |
| break; |
| case 2: |
| fShadowParams.fBiasingConstant = newValue; |
| break; |
| default: |
| break; |
| } |
| } |
| |
| 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; |
| |
| if (fMoveLight) { |
| if (dx != 0 || dy != 0) { |
| this->updateLights(x, y); |
| fLightsChanged = true; |
| this->inval(nullptr); |
| } |
| return true; |
| } |
| |
| if (click->fState == Click::State::kUp_State) { |
| fSelectedRectID = -1; |
| fSelectedSliderID = -1; |
| return true; |
| } |
| |
| if (fSelectedRectID > -1) { |
| fTestRects[fSelectedRectID].fGeometry.offset(dx, dy); |
| |
| fSceneChanged = true; |
| this->inval(nullptr); |
| return true; |
| } |
| |
| if (fSelectedSliderID > -1) { |
| fSliders[fSelectedSliderID].fGeometry.offset(dx, 0); |
| |
| this->updateFromSelectedSlider(); |
| |
| fLightsChanged = true; |
| this->inval(nullptr); |
| return true; |
| } |
| |
| // assume last elements are highest |
| for (int i = kNumTestRects - 1; i >= 0; i--) { |
| if (fTestRects[i].fGeometry.contains(SkRect::MakeXYWH(x, y, 1, 1))) { |
| fSelectedRectID = i; |
| fTestRects[i].fGeometry.offset(dx, dy); |
| |
| fSceneChanged = true; |
| this->inval(nullptr); |
| break; |
| } |
| } |
| |
| for (int i = 0; i <= kNumSliders; i++) { |
| if (fSliders[i].fGeometry.contains(SkRect::MakeXYWH(x, y, 1, 1))) { |
| fSelectedSliderID = i; |
| fSliders[i].fGeometry.offset(dx, 0); |
| |
| this->updateFromSelectedSlider(); |
| |
| fLightsChanged = true; |
| |
| this->inval(nullptr); |
| break; |
| } |
| } |
| |
| return true; |
| } |
| |
| private: |
| static constexpr int kNumTestRects = 3; |
| static constexpr int kNumSliders = 3; |
| |
| static const int kWidth = 400; |
| static const int kHeight = 400; |
| |
| bool fSceneChanged; |
| bool fLightsChanged; |
| bool fMoveLight; |
| bool fClearShadowMaps; |
| |
| struct { |
| SkRect fGeometry; |
| int fDepth; |
| SkColor fColor; |
| } fTestRects[kNumTestRects]; |
| int fSelectedRectID; |
| |
| struct { |
| SkRect fGeometry; |
| SkScalar fOffset; |
| SkScalar fScale; |
| } fSliders[kNumSliders]; |
| int fSelectedSliderID; |
| |
| SkScalar fLightDepth; |
| |
| sk_sp<SkPicture> fPicture; |
| SkShadowParams fShadowParams; |
| sk_sp<SkLights> fLights; |
| |
| typedef SampleView INHERITED; |
| }; |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| static SkView* MyFactory() { return new ShadowingView; } |
| static SkViewRegister reg(MyFactory); |
| |
| #endif |