diff --git a/fuzz/FuzzCanvas.cpp b/fuzz/FuzzCanvas.cpp index fb90c06305..aec9173a16 100644 --- a/fuzz/FuzzCanvas.cpp +++ b/fuzz/FuzzCanvas.cpp @@ -38,6 +38,7 @@ #include "SkDiscretePathEffect.h" #include "SkDisplacementMapEffect.h" #include "SkDropShadowImageFilter.h" +#include "SkGaussianEdgeShader.h" #include "SkGradientShader.h" #include "SkHighContrastFilter.h" #include "SkImageSource.h" @@ -381,8 +382,7 @@ static sk_sp make_fuzz_shader(Fuzz* fuzz, int depth) { } // EFFECTS: case 9: - // Deprecated SkGaussianEdgeShader - return nullptr; + return SkGaussianEdgeShader::Make(); case 10: { constexpr int kMaxColors = 12; SkPoint pts[2]; diff --git a/gm/gaussianedge.cpp b/gm/gaussianedge.cpp new file mode 100644 index 0000000000..eca8495821 --- /dev/null +++ b/gm/gaussianedge.cpp @@ -0,0 +1,222 @@ +/* + * 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 "gm.h" +#include "SkColorFilter.h" +#include "SkGaussianEdgeShader.h" +#include "SkRRect.h" + +//#define VIZ 1 + +#ifdef VIZ +#include "SkStroke.h" + +static void draw_stroke(SkCanvas* canvas, const SkRRect& rr, const SkPaint& p, SkColor color) { + SkPath output; + + if (SkPaint::kFill_Style == p.getStyle()) { + output.addRRect(rr); + } else { + SkPath input; + input.addRRect(rr); + + SkStroke stroke(p); + stroke.strokePath(input, &output); + } + + SkPaint paint; + paint.setStyle(SkPaint::kStroke_Style); + paint.setColor(color); + + canvas->drawPath(output, paint); +} + +static void extract_pts(const SkBitmap& bm, SkTDArray* pts, + int xOff, int width) { + pts->rewind(); + + for (int x = 0; x < width; ++x) { + SkColor color = bm.getColor(xOff+x, 0); + + pts->append()->set(SkIntToScalar(x), 255.0f-SkColorGetB(color)); + if (x > 0 && x < width-1) { + pts->append()->set(SkIntToScalar(x), 255.0f-SkColorGetB(color)); + } + } +} + +static void draw_row(SkCanvas* canvas, int row, int width) { + SkPaint paint; + paint.setAntiAlias(true); + + SkBitmap readback; + + if (!canvas->readPixels(SkIRect::MakeXYWH(0, row, width, 1), &readback)) { + return; + } + + SkTDArray pts; + pts.setReserve(width/3); + + extract_pts(readback, &pts, 0, width/3); + paint.setColor(SK_ColorRED); + canvas->drawPoints(SkCanvas::kLines_PointMode, pts.count(), pts.begin(), paint); + + extract_pts(readback, &pts, width/3, width/3); + paint.setColor(SK_ColorGREEN); + canvas->drawPoints(SkCanvas::kLines_PointMode, pts.count(), pts.begin(), paint); + + extract_pts(readback, &pts, 2*width/3, width/3); + paint.setColor(SK_ColorBLUE); + canvas->drawPoints(SkCanvas::kLines_PointMode, pts.count(), pts.begin(), paint); +} +#endif + +namespace skiagm { + +// This GM exercises the SkGaussianEdgeShader. +// It draws three columns showing filled, stroked, and stroke and filled rendering. +// It draws three rows showing a blur radius smaller than, equal to +// and, finally, double the RRect's corner radius +// In VIZ mode an extra column is drawn showing the blur ramps (they should all line up). +class GaussianEdgeGM : public GM { +public: + GaussianEdgeGM() { + this->setBGColor(SK_ColorWHITE); + } + +protected: + + SkString onShortName() override { + return SkString("gaussianedge"); + } + + SkISize onISize() override { + int numCols = kNumBaseCols; +#ifdef VIZ + numCols++; +#endif + + return SkISize::Make(kPad + numCols*(kCellWidth+kPad), + kPad + kNumRows*(kCellWidth+kPad)); + } + + static void DrawRow(SkCanvas* canvas, int blurRad, int midLine) { + SkAutoCanvasRestore acr(canvas, true); + + SkRRect rrects[kNumBaseCols]; + SkPaint paints[kNumBaseCols]; + + { + const SkRect r = SkRect::MakeIWH(kRRSize, kRRSize); + const SkRRect baseRR = SkRRect::MakeRectXY(r, + SkIntToScalar(kRRRad), + SkIntToScalar(kRRRad)); + + SkPaint basePaint; + basePaint.setAntiAlias(true); + basePaint.setColor(SkColorSetARGB(255, (4 * blurRad) >> 8, (4 * blurRad) & 0xff, 0)); + basePaint.setShader(SkGaussianEdgeShader::Make()); + basePaint.setColorFilter(SkColorFilter::MakeModeFilter(SK_ColorRED, + SkBlendMode::kModulate)); + + //---- + paints[0] = basePaint; + rrects[0] = baseRR; + + //---- + paints[1] = basePaint; + paints[1].setStyle(SkPaint::kStroke_Style); + + rrects[1] = baseRR; + if (blurRad/2.0f < kRRRad) { + rrects[1].inset(blurRad/2.0f, blurRad/2.0f); + paints[1].setStrokeWidth(SkIntToScalar(blurRad)); + } else { + SkScalar inset = kRRRad - 0.5f; + rrects[1].inset(inset, inset); + SkScalar pad = blurRad/2.0f - inset; + paints[1].setStrokeWidth(blurRad + 2.0f * pad); + paints[1].setColor(SkColorSetARGB(255, (4 * blurRad) >> 8, (4 * blurRad) & 0xff, + (int)(4.0f*pad))); + } + + //---- + paints[2] = basePaint; + paints[2].setStyle(SkPaint::kStrokeAndFill_Style); + + rrects[2] = baseRR; + if (blurRad/2.0f < kRRRad) { + rrects[2].inset(blurRad/2.0f, blurRad/2.0f); + paints[2].setStrokeWidth(SkIntToScalar(blurRad)); + } else { + SkScalar inset = kRRRad - 0.5f; + rrects[2].inset(inset, inset); + SkScalar pad = blurRad/2.0f - inset; + paints[2].setStrokeWidth(blurRad + 2.0f * pad); + paints[2].setColor(SkColorSetARGB(255, (4 * blurRad) >> 8, (4 * blurRad) & 0xff, + (int)(4.0f*pad))); + } + } + + //---- + canvas->save(); + // draw the shadows + for (int i = 0; i < kNumBaseCols; ++i) { + canvas->drawRRect(rrects[i], paints[i]); + canvas->translate(SkIntToScalar(kCellWidth+kPad), 0.0f); + } + +#ifdef VIZ + // draw the visualization of the shadow ramps + draw_row(canvas, midLine, 3*(kRRSize+kPad)); +#endif + canvas->restore(); + +#ifdef VIZ + const SkColor colors[kNumBaseCols] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE }; + + // circle back and draw the stroked geometry (they would mess up the viz otherwise) + for (int i = 0; i < kNumBaseCols; ++i) { + draw_stroke(canvas, rrects[i], paints[i], colors[i]); + canvas->translate(SkIntToScalar(kCellWidth+kPad), 0.0f); + } +#endif + } + + void onDraw(SkCanvas* canvas) override { + GrRenderTargetContext* renderTargetContext = + canvas->internal_private_accessTopLayerRenderTargetContext(); + if (!renderTargetContext) { + skiagm::GM::DrawGpuOnlyMessage(canvas); + return; + } + + const int blurRadii[kNumRows] = { kRRRad/2, kRRRad, 2*kRRRad }; + + canvas->translate(SkIntToScalar(kPad), SkIntToScalar(kPad)); + for (int i = 0; i < kNumRows; ++i) { + DrawRow(canvas, blurRadii[i], kPad+(i*kRRSize)+kRRSize/2); + canvas->translate(0.0f, SkIntToScalar(kCellWidth+kPad)); + } + } + +private: + static const int kNumRows = 3; + static const int kNumBaseCols = 3; + static const int kPad = 5; + static const int kRRSize = 256; + static const int kRRRad = 64; + static const int kCellWidth = kRRSize; + + typedef GM INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +DEF_GM(return new GaussianEdgeGM;) +} diff --git a/gm/reveal.cpp b/gm/reveal.cpp index a793b2da54..3e465556ad 100644 --- a/gm/reveal.cpp +++ b/gm/reveal.cpp @@ -9,6 +9,7 @@ #include "sk_tool_utils.h" #include "SkAnimTimer.h" #include "SkBlurMaskFilter.h" +#include "SkGaussianEdgeShader.h" #include "SkRRectsGaussianEdgeMaskFilter.h" #include "SkPath.h" #include "SkPathOps.h" @@ -313,6 +314,7 @@ namespace skiagm { class RevealGM : public GM { public: enum Mode { + kGaussianEdge_Mode, kBlurMask_Mode, kRRectsGaussianEdge_Mode, @@ -384,7 +386,20 @@ protected: // The goal is to replace this clipped draw (which clips the // shadow) with a draw using the geometric clip - if (kBlurMask_Mode == fMode) { + if (kGaussianEdge_Mode == fMode) { + canvas->save(); + clipObj->clip(canvas); + + // Draw with GaussianEdgeShader + SkPaint paint; + paint.setAntiAlias(true); + // G channel is an F6.2 radius + int iBlurRad = (int)(4.0f * fBlurRadius); + paint.setColor(SkColorSetARGB(255, iBlurRad >> 8, iBlurRad & 0xFF, 0)); + paint.setShader(SkGaussianEdgeShader::Make()); + drawObj->draw(canvas, paint); + canvas->restore(); + } else if (kBlurMask_Mode == fMode) { SkPath clippedPath; SkScalar sigma = fBlurRadius / 4.0f; diff --git a/gn/effects.gni b/gn/effects.gni index 61b1b39424..3fd8e531a6 100644 --- a/gn/effects.gni +++ b/gn/effects.gni @@ -38,6 +38,8 @@ skia_effects_sources = [ "$_src/effects/SkEmbossMask_Table.h", "$_src/effects/SkEmbossMaskFilter.cpp", "$_src/effects/SkImageSource.cpp", + "$_src/effects/SkGaussianEdgeShader.cpp", + "$_src/effects/SkGaussianEdgeShader.h", "$_src/effects/SkHighContrastFilter.cpp", "$_src/effects/SkLayerDrawLooper.cpp", "$_src/effects/SkLayerRasterizer.cpp", diff --git a/gn/gm.gni b/gn/gm.gni index 3f670dd5b1..2eb9a1659e 100644 --- a/gn/gm.gni +++ b/gn/gm.gni @@ -135,6 +135,7 @@ gm_sources = [ "$_gm/gammaencodedpremul.cpp", "$_gm/gammatext.cpp", "$_gm/gamut.cpp", + "$_gm/gaussianedge.cpp", "$_gm/getpostextpath.cpp", "$_gm/giantbitmap.cpp", "$_gm/glyph_pos.cpp", diff --git a/samplecode/SampleAndroidShadows.cpp b/samplecode/SampleAndroidShadows.cpp index 0f133404e8..5b070642bb 100644 --- a/samplecode/SampleAndroidShadows.cpp +++ b/samplecode/SampleAndroidShadows.cpp @@ -12,6 +12,7 @@ #include "SkColorFilter.h" #include "SkCamera.h" #include "SkCanvas.h" +#include "SkGaussianEdgeShader.h" #include "SkPath.h" #include "SkPathOps.h" #include "SkPoint3.h" diff --git a/samplecode/SampleShadowUtils.cpp b/samplecode/SampleShadowUtils.cpp index c37d8d2b28..8d8a0aeaf6 100755 --- a/samplecode/SampleShadowUtils.cpp +++ b/samplecode/SampleShadowUtils.cpp @@ -12,6 +12,7 @@ #include "SkColorFilter.h" #include "SkCamera.h" #include "SkCanvas.h" +#include "SkGaussianEdgeShader.h" #include "SkPath.h" #include "SkPathOps.h" #include "SkPoint3.h" diff --git a/src/effects/SkGaussianEdgeShader.cpp b/src/effects/SkGaussianEdgeShader.cpp new file mode 100644 index 0000000000..c710b949d3 --- /dev/null +++ b/src/effects/SkGaussianEdgeShader.cpp @@ -0,0 +1,95 @@ +/* + * 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 "SkGaussianEdgeShader.h" +#include "SkReadBuffer.h" +#include "SkWriteBuffer.h" + +class SkArenaAlloc; + + /** \class SkGaussianEdgeShaderImpl + This subclass of shader applies a Gaussian to shadow edge + + If the primitive supports an implicit distance to the edge, the radius of the blur is specified + by r & g values of the color in 14.2 fixed point. For spot shadows, we increase the stroke width + to set the shadow against the shape. This pad is specified by b, also in 6.2 fixed point. + + When not using implicit distance, then b in the input color represents the input to the + blur function. + */ +class SkGaussianEdgeShaderImpl : public SkShaderBase { +public: + SkGaussianEdgeShaderImpl() {} + + bool isOpaque() const override; + +#if SK_SUPPORT_GPU + sk_sp asFragmentProcessor(const AsFPArgs&) const override; +#endif + + SK_TO_STRING_OVERRIDE() + SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkGaussianEdgeShaderImpl) + +protected: + void flatten(SkWriteBuffer&) const override; + Context* onMakeContext(const ContextRec& rec, SkArenaAlloc* storage) const override { + return nullptr; + } +private: + friend class SkGaussianEdgeShader; + + typedef SkShaderBase INHERITED; +}; + +//////////////////////////////////////////////////////////////////////////// + +#if SK_SUPPORT_GPU + +#include "effects/GrBlurredEdgeFragmentProcessor.h" + +//////////////////////////////////////////////////////////////////////////// + +sk_sp SkGaussianEdgeShaderImpl::asFragmentProcessor(const AsFPArgs&) const { + return GrBlurredEdgeFP::Make(GrBlurredEdgeFP::kGaussian_Mode); +} + +#endif + +//////////////////////////////////////////////////////////////////////////// + +bool SkGaussianEdgeShaderImpl::isOpaque() const { + return false; +} + +//////////////////////////////////////////////////////////////////////////// + +#ifndef SK_IGNORE_TO_STRING +void SkGaussianEdgeShaderImpl::toString(SkString* str) const { + str->appendf("GaussianEdgeShader: ()"); +} +#endif + +sk_sp SkGaussianEdgeShaderImpl::CreateProc(SkReadBuffer& buf) { + return sk_make_sp(); +} + +void SkGaussianEdgeShaderImpl::flatten(SkWriteBuffer& buf) const { +} + +/////////////////////////////////////////////////////////////////////////////// + +sk_sp SkGaussianEdgeShader::Make() { + return sk_make_sp(); +} + +/////////////////////////////////////////////////////////////////////////////// + +SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkGaussianEdgeShader) +SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkGaussianEdgeShaderImpl) +SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END + +/////////////////////////////////////////////////////////////////////////////// diff --git a/src/effects/SkGaussianEdgeShader.h b/src/effects/SkGaussianEdgeShader.h new file mode 100644 index 0000000000..f0554dd0a7 --- /dev/null +++ b/src/effects/SkGaussianEdgeShader.h @@ -0,0 +1,27 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkGaussianEdgeShader_DEFINED +#define SkGaussianEdgeShader_DEFINED + +#include "SkShaderBase.h" + +class SK_API SkGaussianEdgeShader { +public: + /** Returns a shader that applies a Gaussian blur depending on distance to the edge + * Currently this is only useable with Circle and RRect shapes on the GPU backend. + * Raster will draw nothing. + */ + static sk_sp Make(); + + SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP() + +private: + SkGaussianEdgeShader(); // can't be instantiated +}; + +#endif diff --git a/src/ports/SkGlobalInitialization_default.cpp b/src/ports/SkGlobalInitialization_default.cpp index de9b61ae9c..8052417ca8 100644 --- a/src/ports/SkGlobalInitialization_default.cpp +++ b/src/ports/SkGlobalInitialization_default.cpp @@ -22,6 +22,7 @@ #include "SkDisplacementMapEffect.h" #include "SkDropShadowImageFilter.h" #include "../../src/effects/SkEmbossMaskFilter.h" +#include "../../src/effects/SkGaussianEdgeShader.h" #include "SkGradientShader.h" #include "SkHighContrastFilter.h" #include "SkImageSource.h" @@ -91,6 +92,7 @@ void SkFlattenable::PrivateInitializer::InitEffects() { SkGradientShader::InitializeFlattenables(); SkLightingShader::InitializeFlattenables(); SkNormalSource::InitializeFlattenables(); + SkGaussianEdgeShader::InitializeFlattenables(); // PathEffect SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkArcToPathEffect)