/* * 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 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 fPicture; SkShadowParams fShadowParams; sk_sp fLights; typedef SampleView INHERITED; }; ////////////////////////////////////////////////////////////////////////////// static SkView* MyFactory() { return new ShadowingView; } static SkViewRegister reg(MyFactory); #endif