diff options
-rw-r--r-- | fuzz/FuzzCanvas.cpp | 9 | ||||
-rw-r--r-- | gm/reveal.cpp | 516 | ||||
-rw-r--r-- | gn/effects.gni | 2 | ||||
-rw-r--r-- | gn/gm.gni | 1 | ||||
-rw-r--r-- | include/effects/SkRRectsGaussianEdgeMaskFilter.h | 37 | ||||
-rw-r--r-- | src/effects/SkRRectsGaussianEdgeMaskFilter.cpp | 586 | ||||
-rw-r--r-- | src/ports/SkGlobalInitialization_default.cpp | 2 |
7 files changed, 1 insertions, 1152 deletions
diff --git a/fuzz/FuzzCanvas.cpp b/fuzz/FuzzCanvas.cpp index 7cfcc132d5..41e975739b 100644 --- a/fuzz/FuzzCanvas.cpp +++ b/fuzz/FuzzCanvas.cpp @@ -55,7 +55,6 @@ #include "SkPerlinNoiseShader.h" #include "SkPictureImageFilter.h" #include "SkReadBuffer.h" -#include "SkRRectsGaussianEdgeMaskFilter.h" #include "SkTableColorFilter.h" #include "SkTextBlob.h" #include "SkTileImageFilter.h" @@ -489,7 +488,7 @@ static sk_sp<SkPathEffect> make_fuzz_patheffect(Fuzz* fuzz, int depth) { static sk_sp<SkMaskFilter> make_fuzz_maskfilter(Fuzz* fuzz) { int maskfilterType; - fuzz->nextRange(&maskfilterType, 0, 2); + fuzz->nextRange(&maskfilterType, 0, 1); switch (maskfilterType) { case 0: return nullptr; @@ -507,12 +506,6 @@ static sk_sp<SkMaskFilter> make_fuzz_maskfilter(Fuzz* fuzz) { bool respectCTM = flags != 0; return SkMaskFilter::MakeBlur(blurStyle, sigma, occluder, respectCTM); } - case 2: { - SkRRect first, second; - SkScalar radius; - fuzz->next(&first, &second, &radius); - return SkRRectsGaussianEdgeMaskFilter::Make(first, second, radius); - } default: SkASSERT(false); return nullptr; diff --git a/gm/reveal.cpp b/gm/reveal.cpp deleted file mode 100644 index 93c64ae2c2..0000000000 --- a/gm/reveal.cpp +++ /dev/null @@ -1,516 +0,0 @@ -/* - * 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 "sk_tool_utils.h" -#include "SkAnimTimer.h" -#include "SkMaskFilter.h" -#include "SkRRectsGaussianEdgeMaskFilter.h" -#include "SkPath.h" -#include "SkPathOps.h" -#include "SkRRectPriv.h" -#include "SkStroke.h" - -constexpr int kNumCols = 2; -constexpr int kNumRows = 5; -constexpr int kCellSize = 128; -constexpr SkScalar kPad = 8.0f; -constexpr SkScalar kInitialBlurRadius = 8.0f; -constexpr SkScalar kPeriod = 8.0f; -constexpr int kClipOffset = 32; - -/////////////////////////////////////////////////////////////////////////////////////////////////// - -class Object { -public: - virtual ~Object() {} - // When it returns true, this call will have placed a device-space _circle, rect or - // simple circular_ RRect in "rr" - virtual bool asDevSpaceRRect(const SkMatrix& ctm, SkRRect* rr) const = 0; - virtual SkPath asPath(SkScalar inset) const = 0; - virtual void draw(SkCanvas* canvas, const SkPaint& paint) const = 0; - virtual void clip(SkCanvas* canvas) const = 0; - virtual bool contains(const SkRect& r) const = 0; - virtual const SkRect& bounds() const = 0; -}; - -typedef Object* (*PFMakeMthd)(const SkRect& r); - -class RRect : public Object { -public: - RRect(const SkRect& r) { - fRRect = SkRRect::MakeRectXY(r, 4*kPad, 4*kPad); - } - - bool asDevSpaceRRect(const SkMatrix& ctm, SkRRect* rr) const override { - if (!ctm.isSimilarity()) { // the corners have to remain circular - return false; - } - - SkScalar scales[2]; - if (!ctm.getMinMaxScales(scales)) { - return false; - } - - SkASSERT(SkScalarNearlyEqual(scales[0], scales[1])); - - SkRect devRect; - ctm.mapRect(&devRect, fRRect.rect()); - - SkScalar scaledRad = scales[0] * SkRRectPriv::GetSimpleRadii(fRRect).fX; - - *rr = SkRRect::MakeRectXY(devRect, scaledRad, scaledRad); - return true; - } - - SkPath asPath(SkScalar inset) const override { - SkRRect tmp = fRRect; - tmp.inset(inset, inset); - SkPath p; - p.addRRect(tmp); - return p; - } - - void draw(SkCanvas* canvas, const SkPaint& paint) const override { - canvas->drawRRect(fRRect, paint); - } - - void clip(SkCanvas* canvas) const override { - canvas->clipRRect(fRRect); - } - - bool contains(const SkRect& r) const override { - return fRRect.contains(r); - } - - const SkRect& bounds() const override { - return fRRect.getBounds(); - } - - static Object* Make(const SkRect& r) { - return new RRect(r); - } - -private: - SkRRect fRRect; -}; - -class StrokedRRect : public Object { -public: - StrokedRRect(const SkRect& r) { - fRRect = SkRRect::MakeRectXY(r, 2*kPad, 2*kPad); - fStrokedBounds = r.makeOutset(kPad, kPad); - } - - bool asDevSpaceRRect(const SkMatrix& ctm, SkRRect* rr) const override { - return false; - } - - SkPath asPath(SkScalar inset) const override { - SkRRect tmp = fRRect; - tmp.inset(inset, inset); - - // In this case we want the outline of the stroked rrect - SkPaint paint; - paint.setAntiAlias(true); - paint.setStyle(SkPaint::kStroke_Style); - paint.setStrokeWidth(kPad); - - SkPath p, stroked; - p.addRRect(tmp); - SkStroke stroke(paint); - stroke.strokePath(p, &stroked); - return stroked; - } - - void draw(SkCanvas* canvas, const SkPaint& paint) const override { - SkPaint stroke(paint); - stroke.setStyle(SkPaint::kStroke_Style); - stroke.setStrokeWidth(kPad); - - canvas->drawRRect(fRRect, stroke); - } - - void clip(SkCanvas* canvas) const override { - canvas->clipPath(this->asPath(0.0f)); - } - - bool contains(const SkRect& r) const override { - return false; - } - - const SkRect& bounds() const override { - return fStrokedBounds; - } - - static Object* Make(const SkRect& r) { - return new StrokedRRect(r); - } - -private: - SkRRect fRRect; - SkRect fStrokedBounds; -}; - -class Oval : public Object { -public: - Oval(const SkRect& r) { - fRRect = SkRRect::MakeOval(r); - } - - bool asDevSpaceRRect(const SkMatrix& ctm, SkRRect* rr) const override { - if (!ctm.isSimilarity()) { // circles have to remain circles - return false; - } - - SkRect devRect; - ctm.mapRect(&devRect, fRRect.rect()); - *rr = SkRRect::MakeOval(devRect); - return true; - } - - SkPath asPath(SkScalar inset) const override { - SkRRect tmp = fRRect; - tmp.inset(inset, inset); - - SkPath p; - p.addRRect(tmp); - return p; - } - - void draw(SkCanvas* canvas, const SkPaint& paint) const override { - canvas->drawRRect(fRRect, paint); - } - - void clip(SkCanvas* canvas) const override { - canvas->clipRRect(fRRect); - } - - bool contains(const SkRect& r) const override { - return fRRect.contains(r); - } - - const SkRect& bounds() const override { - return fRRect.getBounds(); - } - - static Object* Make(const SkRect& r) { - return new Oval(r); - } - -private: - SkRRect fRRect; -}; - -class Rect : public Object { -public: - Rect(const SkRect& r) : fRect(r) { } - - bool asDevSpaceRRect(const SkMatrix& ctm, SkRRect* rr) const override { - if (!ctm.rectStaysRect()) { - return false; - } - - SkRect devRect; - ctm.mapRect(&devRect, fRect); - *rr = SkRRect::MakeRect(devRect); - return true; - } - - SkPath asPath(SkScalar inset) const override { - SkRect tmp = fRect; - tmp.inset(inset, inset); - - SkPath p; - p.addRect(tmp); - return p; - } - - void draw(SkCanvas* canvas, const SkPaint& paint) const override { - canvas->drawRect(fRect, paint); - } - - void clip(SkCanvas* canvas) const override { - canvas->clipRect(fRect); - } - - bool contains(const SkRect& r) const override { - return fRect.contains(r); - } - - const SkRect& bounds() const override { - return fRect; - } - - static Object* Make(const SkRect& r) { - return new Rect(r); - } - -private: - SkRect fRect; -}; - -class Pentagon : public Object { -public: - Pentagon(const SkRect& r) { - SkPoint points[5] = { - { 0.000000f, -1.000000f }, - { -0.951056f, -0.309017f }, - { -0.587785f, 0.809017f }, - { 0.587785f, 0.809017f }, - { 0.951057f, -0.309017f }, - }; - - SkScalar height = r.height()/2.0f; - SkScalar width = r.width()/2.0f; - - fPath.moveTo(r.centerX() + points[0].fX * width, r.centerY() + points[0].fY * height); - fPath.lineTo(r.centerX() + points[1].fX * width, r.centerY() + points[1].fY * height); - fPath.lineTo(r.centerX() + points[2].fX * width, r.centerY() + points[2].fY * height); - fPath.lineTo(r.centerX() + points[3].fX * width, r.centerY() + points[3].fY * height); - fPath.lineTo(r.centerX() + points[4].fX * width, r.centerY() + points[4].fY * height); - fPath.close(); - } - - bool asDevSpaceRRect(const SkMatrix& ctm, SkRRect* rr) const override { - return false; - } - - SkPath asPath(SkScalar inset) const override { return fPath; } - - void draw(SkCanvas* canvas, const SkPaint& paint) const override { - canvas->drawPath(fPath, paint); - } - - void clip(SkCanvas* canvas) const override { - canvas->clipPath(this->asPath(0.0f)); - } - - bool contains(const SkRect& r) const override { - return false; - } - - const SkRect& bounds() const override { - return fPath.getBounds(); - } - - static Object* Make(const SkRect& r) { - return new Pentagon(r); - } - -private: - SkPath fPath; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -namespace skiagm { - -// This GM attempts to mimic Android's reveal animation -class RevealGM : public GM { -public: - enum Mode { - kBlurMask_Mode, - kRRectsGaussianEdge_Mode, - - kLast_Mode = kRRectsGaussianEdge_Mode - }; - static const int kModeCount = kLast_Mode + 1; - - enum CoverageGeom { - kRect_CoverageGeom, - kRRect_CoverageGeom, - kDRRect_CoverageGeom, - kPath_CoverageGeom, - - kLast_CoverageGeom = kPath_CoverageGeom - }; - static const int kCoverageGeomCount = kLast_CoverageGeom + 1; - - RevealGM() - : fFraction(0.5f) - , fMode(kRRectsGaussianEdge_Mode) - , fPause(false) - , fBlurRadius(kInitialBlurRadius) - , fCoverageGeom(kRect_CoverageGeom) { - this->setBGColor(sk_tool_utils::color_to_565(0xFFCCCCCC)); - } - -protected: - bool runAsBench() const override { return true; } - - SkString onShortName() override { - return SkString("reveal"); - } - - SkISize onISize() override { - return SkISize::Make(kNumCols * kCellSize, kNumRows * kCellSize); - } - - void onDraw(SkCanvas* canvas) override { - PFMakeMthd clipMakes[kNumCols] = { Oval::Make, Rect::Make }; - PFMakeMthd drawMakes[kNumRows] = { - RRect::Make, StrokedRRect::Make, Oval::Make, Rect::Make, Pentagon::Make - }; - - SkPaint strokePaint; - strokePaint.setStyle(SkPaint::kStroke_Style); - strokePaint.setStrokeWidth(0.0f); - - for (int y = 0; y < kNumRows; ++y) { - for (int x = 0; x < kNumCols; ++x) { - SkRect cell = SkRect::MakeXYWH(SkIntToScalar(x*kCellSize), - SkIntToScalar(y*kCellSize), - SkIntToScalar(kCellSize), - SkIntToScalar(kCellSize)); - - canvas->save(); - canvas->clipRect(cell); - - cell.inset(kPad, kPad); - SkPoint clipCenter = SkPoint::Make(cell.centerX() - kClipOffset, - cell.centerY() + kClipOffset); - SkScalar curSize = kCellSize * fFraction; - const SkRect clipRect = SkRect::MakeLTRB(clipCenter.fX - curSize, - clipCenter.fY - curSize, - clipCenter.fX + curSize, - clipCenter.fY + curSize); - - std::unique_ptr<Object> clipObj((*clipMakes[x])(clipRect)); - std::unique_ptr<Object> drawObj((*drawMakes[y])(cell)); - - // The goal is to replace this clipped draw (which clips the - // shadow) with a draw using the geometric clip - if (kBlurMask_Mode == fMode) { - SkPath clippedPath; - - SkScalar sigma = fBlurRadius / 4.0f; - - if (clipObj->contains(drawObj->bounds())) { - clippedPath = drawObj->asPath(2.0f*sigma); - } else { - SkPath drawnPath = drawObj->asPath(2.0f*sigma); - SkPath clipPath = clipObj->asPath(2.0f*sigma); - - SkAssertResult(Op(clipPath, drawnPath, kIntersect_SkPathOp, &clippedPath)); - } - - SkPaint blurPaint; - blurPaint.setAntiAlias(true); - blurPaint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma)); - canvas->drawPath(clippedPath, blurPaint); - } else { - SkASSERT(kRRectsGaussianEdge_Mode == fMode); - - SkRect cover = drawObj->bounds(); - SkAssertResult(cover.intersect(clipObj->bounds())); - - SkPaint paint; - - SkRRect devSpaceClipRR, devSpaceDrawnRR; - - if (clipObj->asDevSpaceRRect(canvas->getTotalMatrix(), &devSpaceClipRR) && - drawObj->asDevSpaceRRect(canvas->getTotalMatrix(), &devSpaceDrawnRR)) { - paint.setMaskFilter(SkRRectsGaussianEdgeMaskFilter::Make(devSpaceClipRR, - devSpaceDrawnRR, - fBlurRadius)); - } - - strokePaint.setColor(SK_ColorBLUE); - - switch (fCoverageGeom) { - case kRect_CoverageGeom: - canvas->drawRect(cover, paint); - canvas->drawRect(cover, strokePaint); - break; - case kRRect_CoverageGeom: { - const SkRRect rrect = SkRRect::MakeRectXY( - cover.makeOutset(10.0f, 10.0f), - 10.0f, 10.0f); - canvas->drawRRect(rrect, paint); - canvas->drawRRect(rrect, strokePaint); - break; - } - case kDRRect_CoverageGeom: { - const SkRRect inner = SkRRect::MakeRectXY(cover.makeInset(10.0f, 10.0f), - 10.0f, 10.0f); - const SkRRect outer = SkRRect::MakeRectXY( - cover.makeOutset(10.0f, 10.0f), - 10.0f, 10.0f); - canvas->drawDRRect(outer, inner, paint); - canvas->drawDRRect(outer, inner, strokePaint); - break; - } - case kPath_CoverageGeom: { - SkPath path; - path.moveTo(cover.fLeft, cover.fTop); - path.lineTo(cover.centerX(), cover.centerY()); - path.lineTo(cover.fRight, cover.fTop); - path.lineTo(cover.fRight, cover.fBottom); - path.lineTo(cover.centerX(), cover.centerY()); - path.lineTo(cover.fLeft, cover.fBottom); - path.close(); - canvas->drawPath(path, paint); - canvas->drawPath(path, strokePaint); - break; - } - } - } - - // Draw the clip and draw objects for reference - strokePaint.setColor(SK_ColorRED); - canvas->drawPath(drawObj->asPath(0.0f), strokePaint); - strokePaint.setColor(SK_ColorGREEN); - canvas->drawPath(clipObj->asPath(0.0f), strokePaint); - - canvas->restore(); - } - } - } - - bool onHandleKey(SkUnichar uni) override { - switch (uni) { - case 'C': - fMode = (Mode)((fMode + 1) % kModeCount); - return true; - case '+': - fBlurRadius += 1.0f; - return true; - case '-': - fBlurRadius = SkTMax(1.0f, fBlurRadius - 1.0f); - return true; - case 'p': - fPause = !fPause; - return true; - case 'G': - fCoverageGeom = (CoverageGeom) ((fCoverageGeom+1) % kCoverageGeomCount); - return true; - } - - return false; - } - - bool onAnimate(const SkAnimTimer& timer) override { - if (!fPause) { - fFraction = timer.pingPong(kPeriod, 0.0f, 0.0f, 1.0f); - } - return true; - } - -private: - SkScalar fFraction; - Mode fMode; - bool fPause; - float fBlurRadius; - CoverageGeom fCoverageGeom; - - typedef GM INHERITED; -}; - -////////////////////////////////////////////////////////////////////////////// - -DEF_GM(return new RevealGM;) -} diff --git a/gn/effects.gni b/gn/effects.gni index 94d56e3b68..8d06a7ab49 100644 --- a/gn/effects.gni +++ b/gn/effects.gni @@ -42,7 +42,6 @@ skia_effects_sources = [ "$_src/effects/SkPaintFlagsDrawFilter.cpp", "$_src/effects/SkPaintImageFilter.cpp", "$_src/effects/SkPictureImageFilter.cpp", - "$_src/effects/SkRRectsGaussianEdgeMaskFilter.cpp", "$_src/effects/SkShaderMaskFilter.cpp", "$_src/effects/SkTableColorFilter.cpp", "$_src/effects/SkTableMaskFilter.cpp", @@ -99,7 +98,6 @@ skia_effects_sources = [ "$_include/effects/SkPaintFlagsDrawFilter.h", "$_include/effects/SkPaintImageFilter.h", "$_include/effects/SkPerlinNoiseShader.h", - "$_include/effects/SkRRectsGaussianEdgeMaskFilter.h", "$_include/effects/SkTableColorFilter.h", "$_include/effects/SkTableMaskFilter.h", "$_include/effects/SkTileImageFilter.h", @@ -258,7 +258,6 @@ gm_sources = [ "$_gm/rects.cpp", "$_gm/repeated_bitmap.cpp", "$_gm/resizeimagefilter.cpp", - "$_gm/reveal.cpp", "$_gm/roundrects.cpp", "$_gm/rrect.cpp", "$_gm/rrectclipdrawpaint.cpp", diff --git a/include/effects/SkRRectsGaussianEdgeMaskFilter.h b/include/effects/SkRRectsGaussianEdgeMaskFilter.h deleted file mode 100644 index cd7effc118..0000000000 --- a/include/effects/SkRRectsGaussianEdgeMaskFilter.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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 SkRRectsGaussianEdgeMaskFilter_DEFINED -#define SkRRectsGaussianEdgeMaskFilter_DEFINED - -#include "SkMaskFilter.h" -#include "SkScalar.h" - -class SkRRect; - -class SK_API SkRRectsGaussianEdgeMaskFilter { -public: - /** Returns a mask filter that applies a Gaussian blur depending on distance to the edge - * of the intersection of two round rects. - * Currently this is only useable with round rects that have the same radii at - * all the corners and for which the x & y radii are equal. - * - * In order to minimize fill the coverage geometry that should be drawn should be no larger - * than the intersection of the bounding boxes of the two round rects. Ambitious users can - * omit the center area of the coverage geometry if it is known to be occluded. - */ - static sk_sp<SkMaskFilter> Make(const SkRRect& first, - const SkRRect& second, - SkScalar radius); - - SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP() - -private: - SkRRectsGaussianEdgeMaskFilter(); // can't be instantiated -}; - -#endif diff --git a/src/effects/SkRRectsGaussianEdgeMaskFilter.cpp b/src/effects/SkRRectsGaussianEdgeMaskFilter.cpp deleted file mode 100644 index 4157f837c5..0000000000 --- a/src/effects/SkRRectsGaussianEdgeMaskFilter.cpp +++ /dev/null @@ -1,586 +0,0 @@ -/* - * 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 "SkMaskFilterBase.h" -#include "SkRRectsGaussianEdgeMaskFilter.h" -#include "SkReadBuffer.h" -#include "SkRRectPriv.h" -#include "SkWriteBuffer.h" - -#if SK_SUPPORT_GPU -#include "GrFragmentProcessor.h" -#endif - - /** \class SkRRectsGaussianEdgeMaskFilterImpl - * This mask filter applies a gaussian edge to the intersection of two round rects. - * The round rects must have the same radii at each corner and the x&y radii - * must also be equal. - */ -class SkRRectsGaussianEdgeMaskFilterImpl : public SkMaskFilterBase { -public: - SkRRectsGaussianEdgeMaskFilterImpl(const SkRRect& first, const SkRRect& second, - SkScalar radius) - : fFirst(first) - , fSecond(second) - , fRadius(radius) { - } - - SkMask::Format getFormat() const override { return SkMask::kA8_Format; } - bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&, - SkIPoint* margin) const override; - - void toString(SkString* str) const override; - SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkRRectsGaussianEdgeMaskFilterImpl) - -protected: - void flatten(SkWriteBuffer&) const override; - -#if SK_SUPPORT_GPU - std::unique_ptr<GrFragmentProcessor> onAsFragmentProcessor(const GrFPArgs& args) const override; - bool onHasFragmentProcessor() const override { return true; } -#endif - -private: - SkRRect fFirst; - SkRRect fSecond; - SkScalar fRadius; - - friend class SkRRectsGaussianEdgeMaskFilter; // for serialization registration system - - typedef SkMaskFilter INHERITED; -}; - -// x & y are in device space -static SkScalar compute_rrect_normalized_dist(const SkRRect& rr, const SkPoint& p, SkScalar rad) { - SkASSERT(rr.getType() == SkRRect::kOval_Type || rr.getType() == SkRRect::kRect_Type || - rr.getType() == SkRRect::kSimple_Type); - SkASSERT(rad > 0.0f); - - SkVector delta = { SkTAbs(p.fX - rr.rect().centerX()), SkTAbs(p.fY - rr.rect().centerY()) }; - - SkScalar halfW = 0.5f * rr.rect().width(); - SkScalar halfH = 0.5f * rr.rect().height(); - SkScalar invRad = 1.0f/rad; - - const SkVector& radii = SkRRectPriv::GetSimpleRadii(rr); - SkASSERT(SkScalarNearlyEqual(radii.fX, radii.fY)); - - switch (rr.getType()) { - case SkRRect::kOval_Type: { - float scaledDist = delta.length() * invRad; - return SkTPin(halfW * invRad - scaledDist, 0.0f, 1.0f); - } - case SkRRect::kRect_Type: { - SkScalar xDist = (halfW - delta.fX) * invRad; - SkScalar yDist = (halfH - delta.fY) * invRad; - - SkVector v = { 1.0f - SkTPin(xDist, 0.0f, 1.0f), 1.0f - SkTPin(yDist, 0.0f, 1.0f) }; - return SkTPin(1.0f - v.length(), 0.0f, 1.0f); - } - case SkRRect::kSimple_Type: { - - //---------------- - // ice-cream-cone fractional distance computation - - // When the blurRadius is larger than the corner radius we want to use it to - // compute the pointy end of the ice cream cone. If it smaller we just want to use - // the center of the corner's circle. When using the blurRadius the inset amount - // can't exceed the halfwidths of the RRect. - SkScalar insetDist = SkTMin(SkTMax(rad, radii.fX), SkTMin(halfW, halfH)); - - // "maxValue" is a correction term for if the blurRadius is larger than the - // size of the RRect. In that case we don't want to go all the way to black. - SkScalar maxValue = insetDist * invRad; - - SkVector coneBottom = { halfW - insetDist, halfH - insetDist }; - SkVector ptInConeSpace = delta - coneBottom; - - SkVector cornerTop = { halfW - radii.fX - coneBottom.fX, halfH - coneBottom.fY }; - SkVector cornerRight = { halfW - coneBottom.fX, halfH - radii.fY - coneBottom.fY }; - - SkScalar cross1 = ptInConeSpace.cross(cornerTop); - SkScalar cross2 = cornerRight.cross(ptInConeSpace); - bool inCone = cross1 > 0.0f && cross2 > 0.0f; - - if (!inCone) { - SkScalar xDist = (halfW - delta.fX) * invRad; - SkScalar yDist = (halfH - delta.fY) * invRad; - - return SkTPin(SkTMin(xDist, yDist), 0.0f, 1.0f); // perpendicular distance - } - - SkVector cornerCenterInConeSpace = { insetDist - radii.fX, insetDist - radii.fY }; - - SkVector connectingVec = ptInConeSpace - cornerCenterInConeSpace; - float distToPtInConeSpace = SkPoint::Normalize(&ptInConeSpace); - - // "a" (i.e., dot(ptInConeSpace, ptInConeSpace) should always be 1.0f since - // ptInConeSpace is now normalized - SkScalar b = 2.0f * ptInConeSpace.dot(connectingVec); - SkScalar c = connectingVec.dot(connectingVec) - radii.fX * radii.fY; - - // lop off negative values that are outside the cone - SkScalar coneDist = SkTMax(0.0f, 0.5f * (-b + SkScalarSqrt(b*b - 4*c))); - - // make the coneDist a fraction of how far it is from the edge to the cone's base - coneDist = (maxValue*coneDist) / (coneDist+distToPtInConeSpace); - return SkTPin(coneDist, 0.0f, 1.0f); - } - default: - return 0.0f; - } -} - -bool SkRRectsGaussianEdgeMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src, - const SkMatrix& matrix, - SkIPoint* margin) const { - - if (src.fFormat != SkMask::kA8_Format) { - return false; - } - - if (margin) { - margin->set(0, 0); - } - - dst->fBounds = src.fBounds; - dst->fRowBytes = dst->fBounds.width(); - dst->fFormat = SkMask::kA8_Format; - dst->fImage = nullptr; - - if (src.fImage) { - size_t dstSize = dst->computeImageSize(); - if (0 == dstSize) { - return false; // too big to allocate, abort - } - - const uint8_t* srcPixels = src.fImage; - uint8_t* dstPixels = dst->fImage = SkMask::AllocImage(dstSize); - - SkPoint basePt = { SkIntToScalar(src.fBounds.fLeft), SkIntToScalar(src.fBounds.fTop) }; - - for (int y = 0; y < dst->fBounds.height(); ++y) { - const uint8_t* srcRow = srcPixels + y * dst->fRowBytes; - uint8_t* dstRow = dstPixels + y*dst->fRowBytes; - - for (int x = 0; x < dst->fBounds.width(); ++x) { - SkPoint curPt = { basePt.fX + x, basePt.fY + y }; - - SkVector vec; - vec.fX = 1.0f - compute_rrect_normalized_dist(fFirst, curPt, fRadius); - vec.fY = 1.0f - compute_rrect_normalized_dist(fSecond, curPt, fRadius); - - SkScalar factor = SkTPin(vec.length(), 0.0f, 1.0f); - factor = exp(-factor * factor * 4.0f) - 0.018f; - SkASSERT(factor >= 0.0f && factor <= 1.0f); - - dstRow[x] = (uint8_t) (factor * srcRow[x]); - } - } - } - - return true; -} - -//////////////////////////////////////////////////////////////////////////// - -#if SK_SUPPORT_GPU - -#include "GrCoordTransform.h" -#include "GrFragmentProcessor.h" -#include "glsl/GrGLSLFragmentProcessor.h" -#include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramDataManager.h" -#include "glsl/GrGLSLUniformHandler.h" -#include "SkGr.h" - -class RRectsGaussianEdgeFP : public GrFragmentProcessor { -public: - enum Mode { - kCircle_Mode, - kRect_Mode, - kSimpleCircular_Mode, - }; - - static std::unique_ptr<GrFragmentProcessor> Make(const SkRRect& first, const SkRRect& second, - SkScalar radius) { - return std::unique_ptr<GrFragmentProcessor>( - new RRectsGaussianEdgeFP(first, second, radius)); - } - - const char* name() const override { return "RRectsGaussianEdgeFP"; } - - std::unique_ptr<GrFragmentProcessor> clone() const override { - return std::unique_ptr<GrFragmentProcessor>(new RRectsGaussianEdgeFP(*this)); - } - - const SkRRect& first() const { return fFirst; } - Mode firstMode() const { return fFirstMode; } - const SkRRect& second() const { return fSecond; } - Mode secondMode() const { return fSecondMode; } - SkScalar radius() const { return fRadius; } - -private: - class GLSLRRectsGaussianEdgeFP : public GrGLSLFragmentProcessor { - public: - GLSLRRectsGaussianEdgeFP() {} - - // This method emits code so that, for each shape, the distance from the edge is returned - // in 'outputName' clamped to 0..1 with positive distance being towards the center of the - // shape. The distance will have been normalized by the radius. - void emitModeCode(Mode mode, - GrGLSLFPFragmentBuilder* fragBuilder, - const char* posName, - const char* sizesName, - const char* radiiName, - const char* radName, - const char* outputName, - const char indices[2]) { // how to access the params for the 2 rrects - - // Positive distance is towards the center of the circle. - // Map all the cases to the lower right quadrant. - fragBuilder->codeAppendf("half2 delta = abs(sk_FragCoord.xy - %s.%s);", - posName, indices); - - switch (mode) { - case kCircle_Mode: - // When a shadow circle gets large we can have some precision issues if - // we do "length(delta)/radius". The scaleDist temporary cuts the - // delta vector down a bit before invoking length. - fragBuilder->codeAppendf("half scaledDist = length(delta/%s);", radName); - fragBuilder->codeAppendf("%s = clamp((%s.%c/%s - scaledDist), 0.0, 1.0);", - outputName, sizesName, indices[0], radName); - break; - case kRect_Mode: - fragBuilder->codeAppendf( - "half2 rectDist = half2(1.0 - clamp((%s.%c - delta.x)/%s, 0.0, 1.0)," - "1.0 - clamp((%s.%c - delta.y)/%s, 0.0, 1.0));", - sizesName, indices[0], radName, - sizesName, indices[1], radName); - fragBuilder->codeAppendf("%s = clamp(1.0 - length(rectDist), 0.0, 1.0);", - outputName); - break; - case kSimpleCircular_Mode: - // For the circular round rect we combine 2 distances: - // the fractional position from the corner inset point to the corner's circle - // the minimum perpendicular distance to the bounding rectangle - // The first distance is used when the pixel is inside the ice-cream-cone-shaped - // portion of a corner. The second is used everywhere else. - // This is intended to approximate the interpolation pattern if we had - // tessellated this geometry into a RRect outside and a rect inside. - - //---------------- - // rect distance computation - fragBuilder->codeAppendf("half xDist = (%s.%c - delta.x) / %s;", - sizesName, indices[0], radName); - fragBuilder->codeAppendf("half yDist = (%s.%c - delta.y) / %s;", - sizesName, indices[1], radName); - fragBuilder->codeAppend("half rectDist = clamp(min(xDist, yDist), 0.0, 1.0);"); - - //---------------- - // ice-cream-cone fractional distance computation - - // When the blurRadius is larger than the corner radius we want to use it to - // compute the pointy end of the ice cream cone. If it smaller we just want to - // use the center of the corner's circle. When using the blurRadius the inset - // amount can't exceed the halfwidths of the RRect. - fragBuilder->codeAppendf("half insetDist = min(max(%s, %s.%c)," - "min(%s.%c, %s.%c));", - radName, radiiName, indices[0], - sizesName, indices[0], sizesName, indices[1]); - // "maxValue" is a correction term for if the blurRadius is larger than the - // size of the RRect. In that case we don't want to go all the way to black. - fragBuilder->codeAppendf("half maxValue = insetDist/%s;", radName); - - fragBuilder->codeAppendf("half2 coneBottom = half2(%s.%c - insetDist," - "%s.%c - insetDist);", - sizesName, indices[0], sizesName, indices[1]); - - fragBuilder->codeAppendf("half2 cornerTop = half2(%s.%c - %s.%c, %s.%c) -" - "coneBottom;", - sizesName, indices[0], radiiName, indices[0], - sizesName, indices[1]); - fragBuilder->codeAppendf("half2 cornerRight = half2(%s.%c, %s.%c - %s.%c) -" - "coneBottom;", - sizesName, indices[0], - sizesName, indices[1], radiiName, indices[1]); - - fragBuilder->codeAppend("half2 ptInConeSpace = delta - coneBottom;"); - fragBuilder->codeAppend("half distToPtInConeSpace = length(ptInConeSpace);"); - - fragBuilder->codeAppend("half cross1 = ptInConeSpace.x * cornerTop.y -" - "ptInConeSpace.y * cornerTop.x;"); - fragBuilder->codeAppend("half cross2 = -ptInConeSpace.x * cornerRight.y + " - "ptInConeSpace.y * cornerRight.x;"); - - fragBuilder->codeAppend("half inCone = step(0.0, cross1) *" - "step(0.0, cross2);"); - - fragBuilder->codeAppendf("half2 cornerCenterInConeSpace = half2(insetDist -" - "%s.%c);", - radiiName, indices[0]); - - fragBuilder->codeAppend("half2 connectingVec = ptInConeSpace -" - "cornerCenterInConeSpace;"); - fragBuilder->codeAppend("ptInConeSpace = normalize(ptInConeSpace);"); - - // "a" (i.e., dot(ptInConeSpace, ptInConeSpace) should always be 1.0f since - // ptInConeSpace is now normalized - fragBuilder->codeAppend("half b = 2.0 * dot(ptInConeSpace, connectingVec);"); - fragBuilder->codeAppendf("half c = dot(connectingVec, connectingVec) - " - "%s.%c * %s.%c;", - radiiName, indices[0], radiiName, indices[0]); - - fragBuilder->codeAppend("half fourAC = 4*c;"); - // This max prevents sqrt(-1) when outside the cone - fragBuilder->codeAppend("half bSq = max(b*b, fourAC);"); - - // lop off negative values that are outside the cone - fragBuilder->codeAppend("half coneDist = " - "max(0.0, 0.5 * (-b + sqrt(bSq - fourAC)));"); - // make the coneDist a fraction of how far it is from the edge to the - // cone's base - fragBuilder->codeAppend("coneDist = (maxValue*coneDist) /" - "(coneDist+distToPtInConeSpace);"); - fragBuilder->codeAppend("coneDist = clamp(coneDist, 0.0, 1.0);"); - - //---------------- - fragBuilder->codeAppendf("%s = mix(rectDist, coneDist, inCone);", outputName); - break; - } - } - - void emitCode(EmitArgs& args) override { - const RRectsGaussianEdgeFP& fp = args.fFp.cast<RRectsGaussianEdgeFP>(); - GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; - GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; - - const char* positionsUniName = nullptr; - fPositionsUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType, - "Positions", &positionsUniName); - const char* sizesUniName = nullptr; - fSizesUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType, - kDefault_GrSLPrecision, "Sizes", &sizesUniName); - const char* radiiUniName = nullptr; - if (fp.fFirstMode == kSimpleCircular_Mode || fp.fSecondMode == kSimpleCircular_Mode) { - fRadiiUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType, - "Radii", &radiiUniName); - } - const char* radUniName = nullptr; - fRadiusUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf_GrSLType, - "Radius", &radUniName); - - fragBuilder->codeAppend("half firstDist;"); - fragBuilder->codeAppend("{"); - this->emitModeCode(fp.firstMode(), fragBuilder, - positionsUniName, sizesUniName, radiiUniName, - radUniName, "firstDist", "xy"); - fragBuilder->codeAppend("}"); - - fragBuilder->codeAppend("half secondDist;"); - fragBuilder->codeAppend("{"); - this->emitModeCode(fp.secondMode(), fragBuilder, - positionsUniName, sizesUniName, radiiUniName, - radUniName, "secondDist", "zw"); - fragBuilder->codeAppend("}"); - - fragBuilder->codeAppend("half2 distVec = half2(1.0 - firstDist, 1.0 - secondDist);"); - - // Finally use the distance to apply the Gaussian edge - fragBuilder->codeAppend("half factor = clamp(length(distVec), 0.0, 1.0);"); - fragBuilder->codeAppend("factor = exp(-factor * factor * 4.0) - 0.018;"); - fragBuilder->codeAppendf("%s = factor*%s;", - args.fOutputColor, args.fInputColor); - } - - static void GenKey(const GrProcessor& proc, const GrShaderCaps&, GrProcessorKeyBuilder* b) { - const RRectsGaussianEdgeFP& fp = proc.cast<RRectsGaussianEdgeFP>(); - - b->add32(fp.firstMode() | (fp.secondMode() << 4)); - } - - protected: - void onSetData(const GrGLSLProgramDataManager& pdman, - const GrFragmentProcessor& proc) override { - const RRectsGaussianEdgeFP& edgeFP = proc.cast<RRectsGaussianEdgeFP>(); - - const SkRRect& first = edgeFP.first(); - const SkRRect& second = edgeFP.second(); - - pdman.set4f(fPositionsUni, - first.getBounds().centerX(), - first.getBounds().centerY(), - second.getBounds().centerX(), - second.getBounds().centerY()); - - pdman.set4f(fSizesUni, - 0.5f * first.rect().width(), - 0.5f * first.rect().height(), - 0.5f * second.rect().width(), - 0.5f * second.rect().height()); - - if (edgeFP.firstMode() == kSimpleCircular_Mode || - edgeFP.secondMode() == kSimpleCircular_Mode) { - // This is a bit of overkill since fX should equal fY for both round rects but it - // makes the shader code simpler. - pdman.set4f(fRadiiUni, - SkRRectPriv::GetSimpleRadii(first).fX, - SkRRectPriv::GetSimpleRadii(first).fY, - SkRRectPriv::GetSimpleRadii(second).fX, - SkRRectPriv::GetSimpleRadii(second).fY); - } - - pdman.set1f(fRadiusUni, edgeFP.radius()); - } - - private: - // The centers of the two round rects (x1, y1, x2, y2) - GrGLSLProgramDataManager::UniformHandle fPositionsUni; - - // The half widths and half heights of the two round rects (w1/2, h1/2, w2/2, h2/2) - // For circles we still upload both width & height to simplify things - GrGLSLProgramDataManager::UniformHandle fSizesUni; - - // The corner radii of the two round rects (rx1, ry1, rx2, ry2) - // We upload both the x&y radii (although they are currently always the same) to make - // the indexing in the shader code simpler. In some future world we could also support - // non-circular corner round rects & ellipses. - GrGLSLProgramDataManager::UniformHandle fRadiiUni; - - // The radius parameters (radius) - GrGLSLProgramDataManager::UniformHandle fRadiusUni; - - typedef GrGLSLFragmentProcessor INHERITED; - }; - - void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override { - GLSLRRectsGaussianEdgeFP::GenKey(*this, caps, b); - } - - RRectsGaussianEdgeFP(const SkRRect& first, const SkRRect& second, SkScalar radius) - : INHERITED(kRRectsGaussianEdgeFP_ClassID, - kCompatibleWithCoverageAsAlpha_OptimizationFlag) - , fFirst(first) - , fSecond(second) - , fRadius(radius) { - - fFirstMode = ComputeMode(fFirst); - fSecondMode = ComputeMode(fSecond); - } - RRectsGaussianEdgeFP(const RRectsGaussianEdgeFP& that) - : INHERITED(kRRectsGaussianEdgeFP_ClassID, - kCompatibleWithCoverageAsAlpha_OptimizationFlag) - , fFirst(that.fFirst) - , fFirstMode(that.fFirstMode) - , fSecond(that.fSecond) - , fSecondMode(that.fSecondMode) - , fRadius(that.fRadius) { - } - - static Mode ComputeMode(const SkRRect& rr) { - if (SkRRectPriv::IsCircle(rr)) { - return kCircle_Mode; - } else if (rr.isRect()) { - return kRect_Mode; - } else { - SkASSERT(SkRRectPriv::IsSimpleCircular(rr)); - return kSimpleCircular_Mode; - } - } - - GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { - return new GLSLRRectsGaussianEdgeFP; - } - - bool onIsEqual(const GrFragmentProcessor& proc) const override { - const RRectsGaussianEdgeFP& edgeFP = proc.cast<RRectsGaussianEdgeFP>(); - return fFirst == edgeFP.fFirst && - fSecond == edgeFP.fSecond && - fRadius == edgeFP.fRadius; - } - - SkRRect fFirst; - Mode fFirstMode; - SkRRect fSecond; - Mode fSecondMode; - SkScalar fRadius; - - typedef GrFragmentProcessor INHERITED; -}; - -//////////////////////////////////////////////////////////////////////////// - -std::unique_ptr<GrFragmentProcessor> -SkRRectsGaussianEdgeMaskFilterImpl::onAsFragmentProcessor(const GrFPArgs& args) const { - return RRectsGaussianEdgeFP::Make(fFirst, fSecond, fRadius); -} - -#endif - -//////////////////////////////////////////////////////////////////////////// - -void SkRRectsGaussianEdgeMaskFilterImpl::toString(SkString* str) const { - str->appendf("RRectsGaussianEdgeMaskFilter: ()"); -} - -sk_sp<SkFlattenable> SkRRectsGaussianEdgeMaskFilterImpl::CreateProc(SkReadBuffer& buf) { - SkRect rect1, rect2; - - buf.readRect(&rect1); - SkScalar xRad1 = buf.readScalar(); - SkScalar yRad1 = buf.readScalar(); - - buf.readRect(&rect2); - SkScalar xRad2 = buf.readScalar(); - SkScalar yRad2 = buf.readScalar(); - - SkScalar radius = buf.readScalar(); - - return sk_make_sp<SkRRectsGaussianEdgeMaskFilterImpl>(SkRRect::MakeRectXY(rect1, xRad1, yRad1), - SkRRect::MakeRectXY(rect2, xRad2, yRad2), - radius); -} - -void SkRRectsGaussianEdgeMaskFilterImpl::flatten(SkWriteBuffer& buf) const { - INHERITED::flatten(buf); - - SkASSERT(SkRRectPriv::EqualRadii(fFirst)); - buf.writeRect(fFirst.rect()); - const SkVector radii1 = SkRRectPriv::GetSimpleRadii(fFirst); - buf.writeScalar(radii1.fX); - buf.writeScalar(radii1.fY); - - SkASSERT(SkRRectPriv::EqualRadii(fSecond)); - buf.writeRect(fSecond.rect()); - const SkVector radii2 = SkRRectPriv::GetSimpleRadii(fSecond); - buf.writeScalar(radii2.fX); - buf.writeScalar(radii2.fY); - - buf.writeScalar(fRadius); -} - -/////////////////////////////////////////////////////////////////////////////// - -sk_sp<SkMaskFilter> SkRRectsGaussianEdgeMaskFilter::Make(const SkRRect& first, - const SkRRect& second, - SkScalar radius) { - if (!SkRRectPriv::EqualRadii(first) || !SkRRectPriv::EqualRadii(second)) { - // we only deal with the shapes where the x & y radii are equal - // and the same for all four corners - return nullptr; - } - - return sk_make_sp<SkRRectsGaussianEdgeMaskFilterImpl>(first, second, radius); -} - -/////////////////////////////////////////////////////////////////////////////// - -SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkRRectsGaussianEdgeMaskFilter) -SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkRRectsGaussianEdgeMaskFilterImpl) -SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END - -/////////////////////////////////////////////////////////////////////////////// diff --git a/src/ports/SkGlobalInitialization_default.cpp b/src/ports/SkGlobalInitialization_default.cpp index 793875ec1e..83a35acd4e 100644 --- a/src/ports/SkGlobalInitialization_default.cpp +++ b/src/ports/SkGlobalInitialization_default.cpp @@ -34,7 +34,6 @@ #include "SkPaintImageFilter.h" #include "SkPerlinNoiseShader.h" #include "SkPictureImageFilter.h" -#include "SkRRectsGaussianEdgeMaskFilter.h" #include "SkShaderMaskFilter.h" #include "SkTableColorFilter.h" #include "SkTileImageFilter.h" @@ -63,7 +62,6 @@ void SkFlattenable::PrivateInitializer::InitEffects() { // MaskFilter SkMaskFilter::InitializeFlattenables(); SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkEmbossMaskFilter) - SkRRectsGaussianEdgeMaskFilter::InitializeFlattenables(); SkShaderMaskFilter::InitializeFlattenables(); // DrawLooper |