2016-08-12 17:40:38 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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"
|
2017-03-10 19:34:51 +00:00
|
|
|
#include "SkAnimTimer.h"
|
2016-08-12 17:40:38 +00:00
|
|
|
#include "SkBlurMask.h"
|
|
|
|
#include "SkBlurMaskFilter.h"
|
2017-02-06 19:36:01 +00:00
|
|
|
#include "SkColorFilter.h"
|
2017-03-10 19:34:51 +00:00
|
|
|
#include "SkCamera.h"
|
2016-08-12 17:40:38 +00:00
|
|
|
#include "SkCanvas.h"
|
2016-08-17 14:59:41 +00:00
|
|
|
#include "SkGaussianEdgeShader.h"
|
2016-08-12 17:40:38 +00:00
|
|
|
#include "SkPath.h"
|
2017-03-10 19:34:51 +00:00
|
|
|
#include "SkPathOps.h"
|
2016-08-12 17:40:38 +00:00
|
|
|
#include "SkPoint3.h"
|
2017-01-13 19:37:37 +00:00
|
|
|
#include "SkShadowUtils.h"
|
2016-08-12 17:40:38 +00:00
|
|
|
#include "SkUtils.h"
|
|
|
|
#include "SkView.h"
|
|
|
|
#include "sk_tool_utils.h"
|
|
|
|
|
2017-01-13 19:37:37 +00:00
|
|
|
#define USE_SHADOW_UTILS
|
|
|
|
|
2016-08-17 14:59:41 +00:00
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2016-08-12 17:40:38 +00:00
|
|
|
class ShadowsView : public SampleView {
|
|
|
|
SkPath fRectPath;
|
|
|
|
SkPath fRRPath;
|
|
|
|
SkPath fCirclePath;
|
2017-01-25 14:39:46 +00:00
|
|
|
SkPath fFunkyRRPath;
|
|
|
|
SkPath fCubicPath;
|
2017-03-10 19:34:51 +00:00
|
|
|
SkPath fSquareRRectPath;
|
|
|
|
SkPath fWideRectPath;
|
|
|
|
SkPath fWideOvalPath;
|
2016-08-12 17:40:38 +00:00
|
|
|
SkPoint3 fLightPos;
|
2017-02-06 19:36:01 +00:00
|
|
|
SkScalar fZDelta;
|
2017-03-10 19:34:51 +00:00
|
|
|
SkScalar fAnimTranslate;
|
2017-04-11 19:29:14 +00:00
|
|
|
SkScalar fAnimAngle;
|
2016-08-12 17:40:38 +00:00
|
|
|
|
|
|
|
bool fShowAmbient;
|
|
|
|
bool fShowSpot;
|
2016-08-17 17:06:18 +00:00
|
|
|
bool fUseAlt;
|
2016-08-12 17:40:38 +00:00
|
|
|
bool fShowObject;
|
2016-10-07 18:44:07 +00:00
|
|
|
bool fIgnoreShadowAlpha;
|
2016-08-12 17:40:38 +00:00
|
|
|
|
|
|
|
public:
|
2017-02-06 19:36:01 +00:00
|
|
|
ShadowsView()
|
2017-03-10 19:34:51 +00:00
|
|
|
: fZDelta(0)
|
|
|
|
, fAnimTranslate(0)
|
2017-04-11 19:29:14 +00:00
|
|
|
, fAnimAngle(0)
|
2017-02-06 19:36:01 +00:00
|
|
|
, fShowAmbient(true)
|
2016-08-12 17:40:38 +00:00
|
|
|
, fShowSpot(true)
|
2016-08-17 17:06:18 +00:00
|
|
|
, fUseAlt(true)
|
2016-10-07 18:44:07 +00:00
|
|
|
, fShowObject(true)
|
|
|
|
, fIgnoreShadowAlpha(false) {}
|
2016-08-12 17:40:38 +00:00
|
|
|
|
|
|
|
protected:
|
|
|
|
void onOnceBeforeDraw() override {
|
|
|
|
fCirclePath.addCircle(0, 0, 50);
|
|
|
|
fRectPath.addRect(SkRect::MakeXYWH(-100, -50, 200, 100));
|
|
|
|
fRRPath.addRRect(SkRRect::MakeRectXY(SkRect::MakeXYWH(-100, -50, 200, 100), 4, 4));
|
2017-01-25 14:39:46 +00:00
|
|
|
fFunkyRRPath.addRoundRect(SkRect::MakeXYWH(-50, -50, SK_Scalar1 * 100, SK_Scalar1 * 100),
|
|
|
|
40 * SK_Scalar1, 20 * SK_Scalar1,
|
|
|
|
SkPath::kCW_Direction);
|
|
|
|
fCubicPath.cubicTo(100 * SK_Scalar1, 50 * SK_Scalar1,
|
|
|
|
20 * SK_Scalar1, 100 * SK_Scalar1,
|
|
|
|
0 * SK_Scalar1, 0 * SK_Scalar1);
|
2017-03-10 19:34:51 +00:00
|
|
|
fSquareRRectPath.addRRect(SkRRect::MakeRectXY(SkRect::MakeXYWH(-50, -50, 100, 100),
|
|
|
|
10, 10));
|
|
|
|
fWideRectPath.addRect(SkRect::MakeXYWH(0, 0, 630, 70));
|
|
|
|
fWideOvalPath.addOval(SkRect::MakeXYWH(0, 0, 630, 70));
|
2017-01-25 14:39:46 +00:00
|
|
|
|
2016-09-12 14:51:04 +00:00
|
|
|
fLightPos = SkPoint3::Make(-700, -700, 2800);
|
2016-08-12 17:40:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// overrides from SkEventSink
|
|
|
|
bool onQuery(SkEvent* evt) override {
|
|
|
|
if (SampleCode::TitleQ(*evt)) {
|
|
|
|
SampleCode::TitleR(evt, "AndroidShadows");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
SkUnichar uni;
|
|
|
|
if (SampleCode::CharQ(*evt, &uni)) {
|
2017-02-14 20:16:46 +00:00
|
|
|
bool handled = false;
|
2016-08-12 17:40:38 +00:00
|
|
|
switch (uni) {
|
2017-02-06 19:36:01 +00:00
|
|
|
case 'W':
|
2016-08-12 17:40:38 +00:00
|
|
|
fShowAmbient = !fShowAmbient;
|
2017-02-14 20:16:46 +00:00
|
|
|
handled = true;
|
2016-08-12 17:40:38 +00:00
|
|
|
break;
|
|
|
|
case 'S':
|
|
|
|
fShowSpot = !fShowSpot;
|
2017-02-14 20:16:46 +00:00
|
|
|
handled = true;
|
2016-08-12 17:40:38 +00:00
|
|
|
break;
|
2016-08-17 17:06:18 +00:00
|
|
|
case 'T':
|
|
|
|
fUseAlt = !fUseAlt;
|
2017-02-14 20:16:46 +00:00
|
|
|
handled = true;
|
2016-08-17 17:06:18 +00:00
|
|
|
break;
|
2016-08-12 17:40:38 +00:00
|
|
|
case 'O':
|
|
|
|
fShowObject = !fShowObject;
|
2017-02-14 20:16:46 +00:00
|
|
|
handled = true;
|
2016-08-12 17:40:38 +00:00
|
|
|
break;
|
|
|
|
case '>':
|
2017-02-06 19:36:01 +00:00
|
|
|
fZDelta += 0.5f;
|
2017-02-14 20:16:46 +00:00
|
|
|
handled = true;
|
2016-08-12 17:40:38 +00:00
|
|
|
break;
|
|
|
|
case '<':
|
2017-02-06 19:36:01 +00:00
|
|
|
fZDelta -= 0.5f;
|
2017-02-14 20:16:46 +00:00
|
|
|
handled = true;
|
2016-08-12 17:40:38 +00:00
|
|
|
break;
|
2016-10-07 18:44:07 +00:00
|
|
|
case '?':
|
|
|
|
fIgnoreShadowAlpha = !fIgnoreShadowAlpha;
|
2017-02-14 20:16:46 +00:00
|
|
|
handled = true;
|
2016-10-07 18:44:07 +00:00
|
|
|
break;
|
2016-08-12 17:40:38 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2017-02-14 20:16:46 +00:00
|
|
|
if (handled) {
|
|
|
|
this->inval(nullptr);
|
|
|
|
return true;
|
|
|
|
}
|
2016-08-12 17:40:38 +00:00
|
|
|
}
|
|
|
|
return this->INHERITED::onQuery(evt);
|
|
|
|
}
|
|
|
|
|
|
|
|
void drawBG(SkCanvas* canvas) {
|
|
|
|
canvas->drawColor(0xFFDDDDDD);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void GetOcclRect(const SkPath& path, SkRect* occlRect) {
|
|
|
|
SkRect pathRect;
|
|
|
|
SkRRect pathRRect;
|
|
|
|
if (path.isOval(&pathRect)) {
|
|
|
|
*occlRect = sk_tool_utils::compute_central_occluder(SkRRect::MakeOval(pathRect));
|
|
|
|
} else if (path.isRRect(&pathRRect)) {
|
|
|
|
*occlRect = sk_tool_utils::compute_central_occluder(pathRRect);
|
|
|
|
} else if (path.isRect(occlRect)) {
|
|
|
|
// the inverse transform for the spot shadow occluder doesn't always get us
|
|
|
|
// back to exactly the same position, so deducting a little slop
|
|
|
|
occlRect->inset(1, 1);
|
|
|
|
} else {
|
|
|
|
*occlRect = SkRect::MakeEmpty();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void drawAmbientShadow(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
|
|
|
|
SkScalar ambientAlpha) {
|
|
|
|
|
|
|
|
if (ambientAlpha <= 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const SkScalar kHeightFactor = 1.f / 128.f;
|
|
|
|
const SkScalar kGeomFactor = 64;
|
|
|
|
|
|
|
|
SkScalar umbraAlpha = 1 / (1 + SkMaxScalar(zValue*kHeightFactor, 0));
|
|
|
|
SkScalar radius = zValue*kHeightFactor*kGeomFactor;
|
|
|
|
|
|
|
|
// occlude blur
|
|
|
|
SkRect occlRect;
|
|
|
|
GetOcclRect(path, &occlRect);
|
|
|
|
sk_sp<SkMaskFilter> mf = SkBlurMaskFilter::Make(kNormal_SkBlurStyle,
|
|
|
|
SkBlurMask::ConvertRadiusToSigma(radius),
|
|
|
|
occlRect,
|
|
|
|
SkBlurMaskFilter::kNone_BlurFlag);
|
|
|
|
|
|
|
|
SkPaint paint;
|
|
|
|
paint.setAntiAlias(true);
|
|
|
|
paint.setMaskFilter(std::move(mf));
|
2016-10-07 18:44:07 +00:00
|
|
|
paint.setColor(SkColorSetARGB(fIgnoreShadowAlpha
|
|
|
|
? 255
|
|
|
|
: (unsigned char)(ambientAlpha*umbraAlpha*255.999f), 0, 0, 0));
|
2016-08-12 17:40:38 +00:00
|
|
|
canvas->drawPath(path, paint);
|
|
|
|
|
|
|
|
// draw occlusion rect
|
2016-08-17 14:59:41 +00:00
|
|
|
#if DRAW_OCCL_RECT
|
2016-08-12 17:40:38 +00:00
|
|
|
SkPaint stroke;
|
|
|
|
stroke.setStyle(SkPaint::kStroke_Style);
|
|
|
|
stroke.setColor(SK_ColorBLUE);
|
|
|
|
canvas->drawRect(occlRect, stroke);
|
2016-08-17 14:59:41 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void drawAmbientShadowAlt(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
|
|
|
|
SkScalar ambientAlpha) {
|
|
|
|
|
|
|
|
if (ambientAlpha <= 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const SkScalar kHeightFactor = 1.f / 128.f;
|
|
|
|
const SkScalar kGeomFactor = 64;
|
|
|
|
|
|
|
|
SkScalar umbraAlpha = 1 / (1 + SkMaxScalar(zValue*kHeightFactor, 0));
|
|
|
|
SkScalar radius = zValue*kHeightFactor*kGeomFactor;
|
2016-09-12 14:51:04 +00:00
|
|
|
// distance to outer of edge of geometry from original shape edge
|
|
|
|
SkScalar offset = radius*umbraAlpha;
|
2016-08-17 14:59:41 +00:00
|
|
|
|
2016-08-29 14:17:47 +00:00
|
|
|
SkRect pathRect;
|
|
|
|
SkRRect pathRRect;
|
2016-09-12 14:51:04 +00:00
|
|
|
SkScalar scaleFactors[2];
|
|
|
|
if (!canvas->getTotalMatrix().getMinMaxScales(scaleFactors)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (scaleFactors[0] != scaleFactors[1] || radius*scaleFactors[0] >= 64 ||
|
2016-08-29 14:17:47 +00:00
|
|
|
!((path.isOval(&pathRect) && pathRect.width() == pathRect.height()) ||
|
|
|
|
(path.isRRect(&pathRRect) && pathRRect.allCornersCircular()) ||
|
|
|
|
path.isRect(&pathRect))) {
|
|
|
|
this->drawAmbientShadow(canvas, path, zValue, ambientAlpha);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-09-12 14:51:04 +00:00
|
|
|
// For all of these, we inset the offset rect by half the radius to get our stroke shape.
|
|
|
|
SkScalar strokeOutset = offset - SK_ScalarHalf*radius;
|
|
|
|
// Make sure we'll have a radius of at least 0.5 after xform
|
|
|
|
if (strokeOutset*scaleFactors[0] < 0.5f) {
|
|
|
|
strokeOutset = 0.5f / scaleFactors[0];
|
|
|
|
}
|
2016-08-17 17:06:18 +00:00
|
|
|
if (path.isOval(nullptr)) {
|
2016-09-12 14:51:04 +00:00
|
|
|
pathRect.outset(strokeOutset, strokeOutset);
|
2016-08-17 17:06:18 +00:00
|
|
|
pathRRect = SkRRect::MakeOval(pathRect);
|
|
|
|
} else if (path.isRect(nullptr)) {
|
2016-09-12 14:51:04 +00:00
|
|
|
pathRect.outset(strokeOutset, strokeOutset);
|
|
|
|
pathRRect = SkRRect::MakeRectXY(pathRect, strokeOutset, strokeOutset);
|
2016-08-17 14:59:41 +00:00
|
|
|
} else {
|
2016-09-12 14:51:04 +00:00
|
|
|
pathRRect.outset(strokeOutset, strokeOutset);
|
2016-08-17 14:59:41 +00:00
|
|
|
}
|
|
|
|
|
2016-08-17 17:06:18 +00:00
|
|
|
SkPaint paint;
|
|
|
|
paint.setAntiAlias(true);
|
2016-08-29 14:17:47 +00:00
|
|
|
paint.setStyle(SkPaint::kStroke_Style);
|
|
|
|
// we outset the stroke a little to cover up AA on the interior edge
|
2016-09-12 14:51:04 +00:00
|
|
|
SkScalar pad = 0.5f;
|
|
|
|
paint.setStrokeWidth(radius + 2*pad);
|
|
|
|
// handle scale of radius and pad due to CTM
|
|
|
|
radius *= scaleFactors[0];
|
|
|
|
pad *= scaleFactors[0];
|
|
|
|
SkASSERT(radius < 16384);
|
|
|
|
SkASSERT(pad < 64);
|
|
|
|
// Convert radius to 14.2 fixed point and place in the R & G components.
|
|
|
|
// Convert pad to 6.2 fixed point and place in the B component.
|
|
|
|
uint16_t iRadius = (uint16_t)(radius*4.0f);
|
|
|
|
unsigned char alpha = (unsigned char)(ambientAlpha*255.999f);
|
2016-10-07 18:44:07 +00:00
|
|
|
paint.setColor(SkColorSetARGB(fIgnoreShadowAlpha ? 255 : alpha,
|
|
|
|
iRadius >> 8, iRadius & 0xff,
|
2016-09-12 14:51:04 +00:00
|
|
|
(unsigned char)(4.0f*pad)));
|
|
|
|
|
2017-02-06 19:36:01 +00:00
|
|
|
paint.setColorFilter(SkColorFilter::MakeModeFilter(SK_ColorBLACK, SkBlendMode::kModulate));
|
2016-09-14 14:04:49 +00:00
|
|
|
paint.setShader(SkGaussianEdgeShader::Make());
|
2016-08-17 17:06:18 +00:00
|
|
|
canvas->drawRRect(pathRRect, paint);
|
2016-08-12 17:40:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void drawSpotShadow(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
|
|
|
|
SkPoint3 lightPos, SkScalar lightWidth, SkScalar spotAlpha) {
|
|
|
|
if (spotAlpha <= 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
SkScalar zRatio = zValue / (lightPos.fZ - zValue);
|
|
|
|
if (zRatio < 0.0f) {
|
|
|
|
zRatio = 0.0f;
|
|
|
|
} else if (zRatio > 0.95f) {
|
|
|
|
zRatio = 0.95f;
|
|
|
|
}
|
2016-09-12 14:51:04 +00:00
|
|
|
SkScalar blurRadius = lightWidth*zRatio;
|
2016-08-12 17:40:38 +00:00
|
|
|
|
|
|
|
// compute the transformation params
|
|
|
|
SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
|
2016-09-12 14:51:04 +00:00
|
|
|
SkMatrix ctmInverse;
|
|
|
|
if (!canvas->getTotalMatrix().invert(&ctmInverse)) {
|
|
|
|
return;
|
2016-08-12 17:40:38 +00:00
|
|
|
}
|
2016-09-12 14:51:04 +00:00
|
|
|
SkPoint lightPos2D = SkPoint::Make(lightPos.fX, lightPos.fY);
|
|
|
|
ctmInverse.mapPoints(&lightPos2D, 1);
|
|
|
|
SkPoint offset = SkPoint::Make(zRatio*(center.fX - lightPos2D.fX),
|
|
|
|
zRatio*(center.fY - lightPos2D.fY));
|
|
|
|
SkScalar scale = lightPos.fZ / (lightPos.fZ - zValue);
|
2016-08-12 17:40:38 +00:00
|
|
|
|
|
|
|
SkAutoCanvasRestore acr(canvas, true);
|
|
|
|
|
|
|
|
sk_sp<SkMaskFilter> mf = SkBlurMaskFilter::Make(kNormal_SkBlurStyle,
|
2016-09-12 14:51:04 +00:00
|
|
|
SkBlurMask::ConvertRadiusToSigma(blurRadius),
|
2016-08-12 17:40:38 +00:00
|
|
|
SkBlurMaskFilter::kNone_BlurFlag);
|
|
|
|
|
|
|
|
SkPaint paint;
|
|
|
|
paint.setAntiAlias(true);
|
|
|
|
paint.setMaskFilter(std::move(mf));
|
2016-10-07 18:44:07 +00:00
|
|
|
paint.setColor(SkColorSetARGB(fIgnoreShadowAlpha
|
|
|
|
? 255
|
|
|
|
: (unsigned char)(spotAlpha*255.999f), 0, 0, 0));
|
2016-08-12 17:40:38 +00:00
|
|
|
|
|
|
|
// apply transformation to shadow
|
|
|
|
canvas->scale(scale, scale);
|
2016-09-12 14:51:04 +00:00
|
|
|
canvas->translate(offset.fX, offset.fY);
|
2016-08-12 17:40:38 +00:00
|
|
|
canvas->drawPath(path, paint);
|
|
|
|
}
|
|
|
|
|
2016-08-17 17:06:18 +00:00
|
|
|
void drawSpotShadowAlt(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
|
|
|
|
SkPoint3 lightPos, SkScalar lightWidth, SkScalar spotAlpha) {
|
|
|
|
if (spotAlpha <= 0) {
|
|
|
|
return;
|
|
|
|
}
|
2016-08-12 17:40:38 +00:00
|
|
|
|
2016-08-17 17:06:18 +00:00
|
|
|
SkScalar zRatio = zValue / (lightPos.fZ - zValue);
|
|
|
|
if (zRatio < 0.0f) {
|
|
|
|
zRatio = 0.0f;
|
|
|
|
} else if (zRatio > 0.95f) {
|
|
|
|
zRatio = 0.95f;
|
|
|
|
}
|
2016-09-12 14:51:04 +00:00
|
|
|
SkScalar radius = 2.0f*lightWidth*zRatio;
|
2016-08-17 17:06:18 +00:00
|
|
|
|
2016-08-29 14:17:47 +00:00
|
|
|
SkRect pathRect;
|
|
|
|
SkRRect pathRRect;
|
2016-09-12 14:51:04 +00:00
|
|
|
SkScalar scaleFactors[2];
|
|
|
|
if (!canvas->getTotalMatrix().getMinMaxScales(scaleFactors)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (scaleFactors[0] != scaleFactors[1] || radius*scaleFactors[0] >= 16384 ||
|
2016-08-29 14:17:47 +00:00
|
|
|
!((path.isOval(&pathRect) && pathRect.width() == pathRect.height()) ||
|
|
|
|
(path.isRRect(&pathRRect) && pathRRect.allCornersCircular()) ||
|
|
|
|
path.isRect(&pathRect))) {
|
|
|
|
this->drawSpotShadow(canvas, path, zValue, lightPos, lightWidth, spotAlpha);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-09-12 14:51:04 +00:00
|
|
|
// For all of these, we need to ensure we have a rrect with radius >= 0.5f in device space
|
2016-10-07 18:44:07 +00:00
|
|
|
const SkScalar minRadius = SK_ScalarHalf/scaleFactors[0];
|
2016-08-17 17:06:18 +00:00
|
|
|
if (path.isOval(nullptr)) {
|
|
|
|
pathRRect = SkRRect::MakeOval(pathRect);
|
|
|
|
} else if (path.isRect(nullptr)) {
|
2016-09-12 14:51:04 +00:00
|
|
|
pathRRect = SkRRect::MakeRectXY(pathRect, minRadius, minRadius);
|
2016-08-17 17:06:18 +00:00
|
|
|
} else {
|
2016-09-12 14:51:04 +00:00
|
|
|
if (pathRRect.getSimpleRadii().fX < minRadius) {
|
|
|
|
pathRRect.setRectXY(pathRRect.rect(), minRadius, minRadius);
|
|
|
|
}
|
2016-08-17 17:06:18 +00:00
|
|
|
}
|
|
|
|
|
2016-09-12 14:51:04 +00:00
|
|
|
// compute the scale and translation for the shadow
|
|
|
|
SkScalar scale = lightPos.fZ / (lightPos.fZ - zValue);
|
|
|
|
SkRRect shadowRRect;
|
|
|
|
pathRRect.transform(SkMatrix::MakeScale(scale, scale), &shadowRRect);
|
|
|
|
SkPoint center = SkPoint::Make(shadowRRect.rect().centerX(), shadowRRect.rect().centerY());
|
|
|
|
SkMatrix ctmInverse;
|
|
|
|
if (!canvas->getTotalMatrix().invert(&ctmInverse)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
SkPoint lightPos2D = SkPoint::Make(lightPos.fX, lightPos.fY);
|
|
|
|
ctmInverse.mapPoints(&lightPos2D, 1);
|
|
|
|
SkPoint offset = SkPoint::Make(zRatio*(center.fX - lightPos2D.fX),
|
|
|
|
zRatio*(center.fY - lightPos2D.fY));
|
2016-08-17 17:06:18 +00:00
|
|
|
SkAutoCanvasRestore acr(canvas, true);
|
|
|
|
|
|
|
|
SkPaint paint;
|
|
|
|
paint.setAntiAlias(true);
|
2016-10-07 18:44:07 +00:00
|
|
|
// We want to extend the stroked area in so that it meets up with the caster
|
|
|
|
// geometry. The stroked geometry will, by definition already be inset half the
|
|
|
|
// stroke width but we also have to account for the scaling.
|
|
|
|
// We also add 1/2 to cover up AA on the interior edge.
|
|
|
|
SkScalar scaleOffset = (scale - 1.0f) * SkTMax(SkTMax(SkTAbs(pathRect.fLeft),
|
|
|
|
SkTAbs(pathRect.fRight)),
|
|
|
|
SkTMax(SkTAbs(pathRect.fTop),
|
|
|
|
SkTAbs(pathRect.fBottom)));
|
|
|
|
SkScalar insetAmount = offset.length() - (0.5f * radius) + scaleOffset + 0.5f;
|
|
|
|
|
2016-08-29 14:17:47 +00:00
|
|
|
// compute area
|
2016-10-07 18:44:07 +00:00
|
|
|
SkScalar strokeWidth = radius + insetAmount;
|
2016-09-12 14:51:04 +00:00
|
|
|
SkScalar strokedArea = 2.0f*strokeWidth*(shadowRRect.width() + shadowRRect.height());
|
|
|
|
SkScalar filledArea = (shadowRRect.height() + radius)*(shadowRRect.width() + radius);
|
2016-08-29 14:17:47 +00:00
|
|
|
// If the area of the stroked geometry is larger than the fill geometry, or
|
|
|
|
// if our pad is too big to convert to 6.2 fixed point, just fill it.
|
2016-10-07 18:44:07 +00:00
|
|
|
if (strokedArea > filledArea) {
|
2016-08-29 14:17:47 +00:00
|
|
|
paint.setStyle(SkPaint::kStrokeAndFill_Style);
|
|
|
|
paint.setStrokeWidth(radius);
|
|
|
|
} else {
|
2016-10-07 18:44:07 +00:00
|
|
|
// Since we can't have unequal strokes, inset the shadow rect so the inner
|
|
|
|
// and outer edges of the stroke will land where we want.
|
|
|
|
SkRect insetRect = shadowRRect.rect().makeInset(insetAmount/2.0f, insetAmount/2.0f);
|
|
|
|
SkScalar insetRad = SkTMax(shadowRRect.getSimpleRadii().fX - insetAmount/2.0f,
|
|
|
|
minRadius);
|
|
|
|
|
|
|
|
shadowRRect = SkRRect::MakeRectXY(insetRect, insetRad, insetRad);
|
2016-08-29 14:17:47 +00:00
|
|
|
paint.setStyle(SkPaint::kStroke_Style);
|
|
|
|
paint.setStrokeWidth(strokeWidth);
|
|
|
|
}
|
2017-02-06 19:36:01 +00:00
|
|
|
paint.setColorFilter(SkColorFilter::MakeModeFilter(SK_ColorBLACK, SkBlendMode::kModulate));
|
2016-09-14 14:04:49 +00:00
|
|
|
paint.setShader(SkGaussianEdgeShader::Make());
|
2016-08-17 17:06:18 +00:00
|
|
|
// handle scale of radius due to CTM
|
2016-09-12 14:51:04 +00:00
|
|
|
radius *= scaleFactors[0];
|
|
|
|
// don't need to scale pad as it was computed from the transformed offset
|
|
|
|
SkASSERT(radius < 16384);
|
2016-10-07 18:44:07 +00:00
|
|
|
SkScalar pad = 0;
|
2016-08-29 14:17:47 +00:00
|
|
|
SkASSERT(pad < 64);
|
2016-09-12 14:51:04 +00:00
|
|
|
// Convert radius to 14.2 fixed point and place in the R & G components.
|
|
|
|
// Convert pad to 6.2 fixed point and place in the B component.
|
|
|
|
uint16_t iRadius = (uint16_t)(radius*4.0f);
|
|
|
|
unsigned char alpha = (unsigned char)(spotAlpha*255.999f);
|
2016-10-07 18:44:07 +00:00
|
|
|
paint.setColor(SkColorSetARGB(fIgnoreShadowAlpha ? 255 : alpha,
|
|
|
|
iRadius >> 8, iRadius & 0xff,
|
2016-09-12 14:51:04 +00:00
|
|
|
(unsigned char)(4.0f*pad)));
|
2016-08-17 17:06:18 +00:00
|
|
|
|
|
|
|
// apply transformation to shadow
|
|
|
|
canvas->translate(offset.fX, offset.fY);
|
2016-09-12 14:51:04 +00:00
|
|
|
canvas->drawRRect(shadowRRect, paint);
|
2016-08-17 17:06:18 +00:00
|
|
|
}
|
|
|
|
|
2017-04-11 19:29:14 +00:00
|
|
|
void drawShadowedPath(SkCanvas* canvas, const SkPath& path,
|
|
|
|
std::function<SkScalar(SkScalar, SkScalar)> zFunc,
|
2016-08-17 17:06:18 +00:00
|
|
|
const SkPaint& paint, SkScalar ambientAlpha,
|
|
|
|
const SkPoint3& lightPos, SkScalar lightWidth, SkScalar spotAlpha) {
|
2017-01-13 19:37:37 +00:00
|
|
|
#ifdef USE_SHADOW_UTILS
|
2017-04-11 19:29:14 +00:00
|
|
|
SkScalar zValue = zFunc(0, 0);
|
2016-10-31 20:06:51 +00:00
|
|
|
if (fUseAlt) {
|
|
|
|
if (fShowAmbient) {
|
|
|
|
this->drawAmbientShadowAlt(canvas, path, zValue, ambientAlpha);
|
|
|
|
}
|
|
|
|
if (fShowSpot) {
|
|
|
|
this->drawSpotShadowAlt(canvas, path, zValue, lightPos, lightWidth, spotAlpha);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!fShowAmbient) {
|
|
|
|
ambientAlpha = 0;
|
|
|
|
}
|
|
|
|
if (!fShowSpot) {
|
|
|
|
spotAlpha = 0;
|
|
|
|
}
|
2017-03-27 18:25:29 +00:00
|
|
|
|
2017-04-11 19:29:14 +00:00
|
|
|
//SkShadowUtils::DrawShadow(canvas, path,
|
|
|
|
// zValue,
|
|
|
|
// lightPos, lightWidth,
|
|
|
|
// ambientAlpha, spotAlpha, SK_ColorBLACK);
|
|
|
|
SkShadowUtils::DrawUncachedShadow(canvas, path, zFunc,
|
|
|
|
lightPos, lightWidth,
|
|
|
|
ambientAlpha, spotAlpha, SK_ColorBLACK);
|
2016-10-31 20:06:51 +00:00
|
|
|
}
|
|
|
|
#else
|
2016-08-12 17:40:38 +00:00
|
|
|
if (fShowAmbient) {
|
2016-08-17 17:06:18 +00:00
|
|
|
if (fUseAlt) {
|
|
|
|
this->drawAmbientShadowAlt(canvas, path, zValue, ambientAlpha);
|
2016-08-17 14:59:41 +00:00
|
|
|
} else {
|
2016-08-17 17:06:18 +00:00
|
|
|
this->drawAmbientShadow(canvas, path, zValue, ambientAlpha);
|
2016-08-17 14:59:41 +00:00
|
|
|
}
|
2016-08-12 17:40:38 +00:00
|
|
|
}
|
|
|
|
if (fShowSpot) {
|
2016-08-17 17:06:18 +00:00
|
|
|
if (fUseAlt) {
|
|
|
|
this->drawSpotShadowAlt(canvas, path, zValue, lightPos, lightWidth, spotAlpha);
|
|
|
|
} else {
|
|
|
|
this->drawSpotShadow(canvas, path, zValue, lightPos, lightWidth, spotAlpha);
|
|
|
|
}
|
2016-08-12 17:40:38 +00:00
|
|
|
}
|
2016-10-31 20:06:51 +00:00
|
|
|
#endif
|
|
|
|
|
2016-08-12 17:40:38 +00:00
|
|
|
if (fShowObject) {
|
|
|
|
canvas->drawPath(path, paint);
|
2016-10-07 18:44:07 +00:00
|
|
|
} else {
|
|
|
|
SkPaint strokePaint;
|
|
|
|
|
|
|
|
strokePaint.setColor(paint.getColor());
|
|
|
|
strokePaint.setStyle(SkPaint::kStroke_Style);
|
|
|
|
|
|
|
|
canvas->drawPath(path, strokePaint);
|
2016-08-12 17:40:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void onDrawContent(SkCanvas* canvas) override {
|
|
|
|
this->drawBG(canvas);
|
2016-09-12 14:51:04 +00:00
|
|
|
const SkScalar kLightWidth = 2800;
|
2016-08-17 17:06:18 +00:00
|
|
|
const SkScalar kAmbientAlpha = 0.25f;
|
|
|
|
const SkScalar kSpotAlpha = 0.25f;
|
2016-08-12 17:40:38 +00:00
|
|
|
|
|
|
|
SkPaint paint;
|
|
|
|
paint.setAntiAlias(true);
|
|
|
|
|
2016-08-17 17:06:18 +00:00
|
|
|
SkPoint3 lightPos = fLightPos;
|
2017-04-20 19:48:37 +00:00
|
|
|
lightPos.fX = canvas->getBaseLayerSize().fWidth * 0.5f;
|
2016-08-17 17:06:18 +00:00
|
|
|
|
2016-08-12 17:40:38 +00:00
|
|
|
paint.setColor(SK_ColorWHITE);
|
|
|
|
canvas->translate(200, 90);
|
2016-08-17 17:06:18 +00:00
|
|
|
lightPos.fX += 200;
|
|
|
|
lightPos.fY += 90;
|
2017-04-11 19:29:14 +00:00
|
|
|
SkScalar zValue = SkTMax(1.0f, 2 + fZDelta);
|
|
|
|
std::function<SkScalar(SkScalar, SkScalar)> zFunc =
|
|
|
|
[zValue](SkScalar, SkScalar) { return zValue; };
|
|
|
|
this->drawShadowedPath(canvas, fRRPath, zFunc, paint, kAmbientAlpha,
|
2016-08-17 17:06:18 +00:00
|
|
|
lightPos, kLightWidth, kSpotAlpha);
|
2016-08-12 17:40:38 +00:00
|
|
|
|
|
|
|
paint.setColor(SK_ColorRED);
|
|
|
|
canvas->translate(250, 0);
|
2016-08-17 17:06:18 +00:00
|
|
|
lightPos.fX += 250;
|
2017-04-11 19:29:14 +00:00
|
|
|
zValue = SkTMax(1.0f, 4 + fZDelta);
|
|
|
|
zFunc = [zValue](SkScalar, SkScalar) { return zValue; };
|
|
|
|
this->drawShadowedPath(canvas, fRectPath, zFunc, paint, kAmbientAlpha,
|
2016-08-17 17:06:18 +00:00
|
|
|
lightPos, kLightWidth, kSpotAlpha);
|
2016-08-12 17:40:38 +00:00
|
|
|
|
|
|
|
paint.setColor(SK_ColorBLUE);
|
|
|
|
canvas->translate(-250, 110);
|
2016-08-17 17:06:18 +00:00
|
|
|
lightPos.fX -= 250;
|
|
|
|
lightPos.fY += 110;
|
2017-04-20 19:48:37 +00:00
|
|
|
zValue = SkTMax(1.0f, 12 + fZDelta);
|
2017-04-11 19:29:14 +00:00
|
|
|
zFunc = [zValue](SkScalar, SkScalar) { return zValue; };
|
|
|
|
this->drawShadowedPath(canvas, fCirclePath, zFunc, paint, kAmbientAlpha,
|
2016-08-17 17:06:18 +00:00
|
|
|
lightPos, kLightWidth, 0.5f);
|
2016-08-17 14:59:41 +00:00
|
|
|
|
|
|
|
paint.setColor(SK_ColorGREEN);
|
|
|
|
canvas->translate(250, 0);
|
2016-08-17 17:06:18 +00:00
|
|
|
lightPos.fX += 250;
|
2017-04-11 19:29:14 +00:00
|
|
|
zValue = SkTMax(1.0f, 64 + fZDelta);
|
|
|
|
zFunc = [zValue](SkScalar, SkScalar) { return zValue; };
|
|
|
|
this->drawShadowedPath(canvas, fRRPath, zFunc, paint, kAmbientAlpha,
|
2016-08-17 17:06:18 +00:00
|
|
|
lightPos, kLightWidth, kSpotAlpha);
|
2017-01-25 14:39:46 +00:00
|
|
|
|
|
|
|
paint.setColor(SK_ColorYELLOW);
|
|
|
|
canvas->translate(-250, 110);
|
|
|
|
lightPos.fX -= 250;
|
|
|
|
lightPos.fY += 110;
|
2017-04-11 19:29:14 +00:00
|
|
|
zValue = SkTMax(1.0f, 8 + fZDelta);
|
|
|
|
zFunc = [zValue](SkScalar, SkScalar) { return zValue; };
|
|
|
|
this->drawShadowedPath(canvas, fFunkyRRPath, zFunc, paint, kAmbientAlpha,
|
2017-01-25 14:39:46 +00:00
|
|
|
lightPos, kLightWidth, kSpotAlpha);
|
|
|
|
|
|
|
|
paint.setColor(SK_ColorCYAN);
|
|
|
|
canvas->translate(250, 0);
|
|
|
|
lightPos.fX += 250;
|
2017-04-11 19:29:14 +00:00
|
|
|
zValue = SkTMax(1.0f, 16 + fZDelta);
|
|
|
|
zFunc = [zValue](SkScalar, SkScalar) { return zValue; };
|
|
|
|
this->drawShadowedPath(canvas, fCubicPath, zFunc, paint,
|
Revert "Revert "Create new inset algorithm for spot shadows""
This reverts commit e7c85c45c4c0a97adc6711bb12ecacc36af4ba11.
Reason for revert: <INSERT REASONING HERE>
Original change's description:
> Revert "Create new inset algorithm for spot shadows"
>
> This reverts commit e5f5bf5175e426ebb6aa234f4387831c898f20ad.
>
> Reason for revert: Breaking a bunch of bots. e.g:
>
> https://luci-milo.appspot.com/swarming/task/3519cae0a03c7b10/steps/dm/0/stdout
>
> Original change's description:
> > Create new inset algorithm for spot shadows
> >
> > BUG=skia:
> >
> > Change-Id: If7c67c2a5b9beea28f86d13362a5156b46394d0e
> > Reviewed-on: https://skia-review.googlesource.com/9875
> > Commit-Queue: Ravi Mistry <rmistry@google.com>
> > Reviewed-by: Brian Salomon <bsalomon@google.com>
> > Reviewed-by: Robert Phillips <robertphillips@google.com>
> >
>
> TBR=jvanverth@google.com,bsalomon@google.com,rmistry@google.com,robertphillips@google.com,msarett@google.com,reviews@skia.org
> NOPRESUBMIT=true
> NOTREECHECKS=true
> NOTRY=true
> BUG=skia:
>
> Change-Id: I3d119ff631dbb1a41f873b9c8753d542ec91254e
> Reviewed-on: https://skia-review.googlesource.com/10112
> Reviewed-by: Brian Salomon <bsalomon@google.com>
> Commit-Queue: Brian Salomon <bsalomon@google.com>
>
TBR=jvanverth@google.com,bsalomon@google.com,rmistry@google.com,msarett@google.com,robertphillips@google.com,reviews@skia.org
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=skia:
Change-Id: Ib3998300606d3a2e2fb3a14b2088cfad48363501
Reviewed-on: https://skia-review.googlesource.com/10113
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
2017-03-24 16:07:20 +00:00
|
|
|
kAmbientAlpha, lightPos, kLightWidth, kSpotAlpha);
|
2017-03-10 19:34:51 +00:00
|
|
|
|
|
|
|
// circular reveal
|
|
|
|
SkPath tmpPath;
|
|
|
|
SkPath tmpClipPath;
|
|
|
|
tmpClipPath.addCircle(fAnimTranslate, 0, 60);
|
|
|
|
Op(fSquareRRectPath, tmpClipPath, kIntersect_SkPathOp, &tmpPath);
|
|
|
|
|
|
|
|
paint.setColor(SK_ColorMAGENTA);
|
|
|
|
canvas->translate(-125, 60);
|
|
|
|
lightPos.fX -= 125;
|
|
|
|
lightPos.fY += 60;
|
2017-04-11 19:29:14 +00:00
|
|
|
zValue = SkTMax(1.0f, 32 + fZDelta);
|
|
|
|
zFunc = [zValue](SkScalar, SkScalar) { return zValue; };
|
|
|
|
this->drawShadowedPath(canvas, tmpPath, zFunc, paint, .1f,
|
2017-03-10 19:34:51 +00:00
|
|
|
lightPos, kLightWidth, .5f);
|
|
|
|
|
|
|
|
// perspective paths
|
|
|
|
SkPoint pivot = SkPoint::Make(fWideRectPath.getBounds().width()/2,
|
|
|
|
fWideRectPath.getBounds().height()/2);
|
2017-04-11 19:29:14 +00:00
|
|
|
SkPoint translate = SkPoint::Make(100, 450);
|
2017-03-10 19:34:51 +00:00
|
|
|
paint.setColor(SK_ColorWHITE);
|
|
|
|
Sk3DView view;
|
|
|
|
view.save();
|
2017-04-11 19:29:14 +00:00
|
|
|
view.rotateX(fAnimAngle);
|
2017-03-10 19:34:51 +00:00
|
|
|
SkMatrix persp;
|
|
|
|
view.getMatrix(&persp);
|
|
|
|
persp.preTranslate(-pivot.fX, -pivot.fY);
|
|
|
|
persp.postTranslate(pivot.fX + translate.fX, pivot.fY + translate.fY);
|
|
|
|
canvas->setMatrix(persp);
|
|
|
|
lightPos = fLightPos;
|
|
|
|
lightPos.fX += pivot.fX + translate.fX;
|
|
|
|
lightPos.fY += pivot.fY + translate.fY;
|
2017-04-11 19:29:14 +00:00
|
|
|
zValue = SkTMax(1.0f, 16 + fZDelta);
|
|
|
|
SkScalar radians = SkDegreesToRadians(fAnimAngle);
|
|
|
|
zFunc = [zValue, pivot, radians](SkScalar x, SkScalar y) {
|
|
|
|
return SkScalarSin(-radians)*y +
|
|
|
|
zValue - SkScalarSin(-radians)*pivot.fY;
|
|
|
|
};
|
|
|
|
this->drawShadowedPath(canvas, fWideRectPath, zFunc, paint, .1f,
|
2017-03-10 19:34:51 +00:00
|
|
|
lightPos, kLightWidth, .5f);
|
|
|
|
|
|
|
|
pivot = SkPoint::Make(fWideOvalPath.getBounds().width() / 2,
|
|
|
|
fWideOvalPath.getBounds().height() / 2);
|
2017-04-11 19:29:14 +00:00
|
|
|
translate = SkPoint::Make(100, 600);
|
2017-03-10 19:34:51 +00:00
|
|
|
view.restore();
|
2017-04-11 19:29:14 +00:00
|
|
|
view.rotateY(fAnimAngle);
|
2017-03-10 19:34:51 +00:00
|
|
|
view.getMatrix(&persp);
|
|
|
|
persp.preTranslate(-pivot.fX, -pivot.fY);
|
|
|
|
persp.postTranslate(pivot.fX + translate.fX, pivot.fY + translate.fY);
|
|
|
|
canvas->setMatrix(persp);
|
|
|
|
lightPos = fLightPos;
|
|
|
|
lightPos.fX += pivot.fX + translate.fX;
|
|
|
|
lightPos.fY += pivot.fY + translate.fY;
|
2017-04-11 19:29:14 +00:00
|
|
|
zValue = SkTMax(1.0f, 32 + fZDelta);
|
|
|
|
zFunc = [zValue, pivot, radians](SkScalar x, SkScalar y) {
|
|
|
|
return -SkScalarSin(radians)*x +
|
|
|
|
zValue + SkScalarSin(radians)*pivot.fX;
|
|
|
|
};
|
|
|
|
this->drawShadowedPath(canvas, fWideOvalPath, zFunc, paint, .1f,
|
2017-03-10 19:34:51 +00:00
|
|
|
lightPos, kLightWidth, .5f);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool onAnimate(const SkAnimTimer& timer) override {
|
Revert "Revert "Create new inset algorithm for spot shadows""
This reverts commit e7c85c45c4c0a97adc6711bb12ecacc36af4ba11.
Reason for revert: <INSERT REASONING HERE>
Original change's description:
> Revert "Create new inset algorithm for spot shadows"
>
> This reverts commit e5f5bf5175e426ebb6aa234f4387831c898f20ad.
>
> Reason for revert: Breaking a bunch of bots. e.g:
>
> https://luci-milo.appspot.com/swarming/task/3519cae0a03c7b10/steps/dm/0/stdout
>
> Original change's description:
> > Create new inset algorithm for spot shadows
> >
> > BUG=skia:
> >
> > Change-Id: If7c67c2a5b9beea28f86d13362a5156b46394d0e
> > Reviewed-on: https://skia-review.googlesource.com/9875
> > Commit-Queue: Ravi Mistry <rmistry@google.com>
> > Reviewed-by: Brian Salomon <bsalomon@google.com>
> > Reviewed-by: Robert Phillips <robertphillips@google.com>
> >
>
> TBR=jvanverth@google.com,bsalomon@google.com,rmistry@google.com,robertphillips@google.com,msarett@google.com,reviews@skia.org
> NOPRESUBMIT=true
> NOTREECHECKS=true
> NOTRY=true
> BUG=skia:
>
> Change-Id: I3d119ff631dbb1a41f873b9c8753d542ec91254e
> Reviewed-on: https://skia-review.googlesource.com/10112
> Reviewed-by: Brian Salomon <bsalomon@google.com>
> Commit-Queue: Brian Salomon <bsalomon@google.com>
>
TBR=jvanverth@google.com,bsalomon@google.com,rmistry@google.com,msarett@google.com,robertphillips@google.com,reviews@skia.org
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=skia:
Change-Id: Ib3998300606d3a2e2fb3a14b2088cfad48363501
Reviewed-on: https://skia-review.googlesource.com/10113
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
2017-03-24 16:07:20 +00:00
|
|
|
fAnimTranslate = timer.pingPong(30, 0, 200, -200);
|
2017-04-11 19:29:14 +00:00
|
|
|
fAnimAngle = timer.pingPong(15, 0, 0, 20);
|
2017-03-10 19:34:51 +00:00
|
|
|
|
|
|
|
return true;
|
2016-08-12 17:40:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
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;
|
|
|
|
|
|
|
|
if (dx != 0 || dy != 0) {
|
|
|
|
fLightPos.fX += dx;
|
|
|
|
fLightPos.fY += dy;
|
|
|
|
this->inval(nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2017-02-14 20:16:46 +00:00
|
|
|
typedef SampleView INHERITED;
|
2016-08-12 17:40:38 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
static SkView* MyFactory() { return new ShadowsView; }
|
|
|
|
static SkViewRegister reg(MyFactory);
|