Index: gm/occludedrrectblur.cpp |
diff --git a/gm/occludedrrectblur.cpp b/gm/occludedrrectblur.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..d73867a9ecc2e312beaf097e808b7115572781af |
--- /dev/null |
+++ b/gm/occludedrrectblur.cpp |
@@ -0,0 +1,389 @@ |
+/* |
+ * 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 "SkBlurMaskFilter.h" |
+#include "SkRRect.h" |
+ |
+static SkRect offset_center_to(const SkIRect& src, SkScalar x, SkScalar y) { |
+ SkScalar halfW = 0.5f * src.width(); |
+ SkScalar halfH = 0.5f * src.height(); |
+ |
+ return SkRect::MakeLTRB(x - halfW, y - halfH, x + halfW, y + halfH); |
+} |
+ |
+// compute the intersection point between the diagonal and the ellipse in the |
+// lower right corner |
+static SkPoint intersection(SkScalar w, SkScalar h) { |
+ SkASSERT(w > 0.0f || h > 0.0f); |
+ |
+ return SkPoint::Make(w / SK_ScalarSqrt2, h / SK_ScalarSqrt2); |
+} |
+ |
+// Use the intersection of the corners' diagonals with their ellipses to shrink |
+// the bounding rect |
+static SkRect compute_central_occluder(const SkRRect& rr) { |
+ const SkRect r = rr.getBounds(); |
+ |
+ SkScalar newL = r.fLeft, newT = r.fTop, newR = r.fRight, newB = r.fBottom; |
+ |
+ SkVector radii = rr.radii(SkRRect::kUpperLeft_Corner); |
+ if (!radii.isZero()) { |
+ SkPoint p = intersection(radii.fX, radii.fY); |
+ |
+ newL = SkTMax(newL, r.fLeft + radii.fX - p.fX); |
+ newT = SkTMax(newT, r.fTop + radii.fY - p.fY); |
+ } |
+ |
+ radii = rr.radii(SkRRect::kUpperRight_Corner); |
+ if (!radii.isZero()) { |
+ SkPoint p = intersection(radii.fX, radii.fY); |
+ |
+ newR = SkTMin(newR, r.fRight + p.fX - radii.fX); |
+ newT = SkTMax(newT, r.fTop + radii.fY - p.fY); |
+ } |
+ |
+ radii = rr.radii(SkRRect::kLowerRight_Corner); |
+ if (!radii.isZero()) { |
+ SkPoint p = intersection(radii.fX, radii.fY); |
+ |
+ newR = SkTMin(newR, r.fRight + p.fX - radii.fX); |
+ newB = SkTMin(newB, r.fBottom - radii.fY + p.fY); |
+ } |
+ |
+ radii = rr.radii(SkRRect::kLowerLeft_Corner); |
+ if (!radii.isZero()) { |
+ SkPoint p = intersection(radii.fX, radii.fY); |
+ |
+ newL = SkTMax(newL, r.fLeft + radii.fX - p.fX); |
+ newB = SkTMin(newB, r.fBottom - radii.fY + p.fY); |
+ } |
+ |
+ return SkRect::MakeLTRB(newL, newT, newR, newB); |
+} |
+ |
+// The widest inset rect |
+static SkRect compute_widest_occluder(const SkRRect& rr) { |
+ SkRect r = rr.getBounds(); |
+ |
+ const SkVector& ul = rr.radii(SkRRect::kUpperLeft_Corner); |
+ const SkVector& ur = rr.radii(SkRRect::kUpperRight_Corner); |
+ const SkVector& lr = rr.radii(SkRRect::kLowerRight_Corner); |
+ const SkVector& ll = rr.radii(SkRRect::kLowerLeft_Corner); |
+ |
+ SkScalar maxT = SkTMax(ul.fY, ur.fY); |
+ SkScalar maxB = SkTMax(ll.fY, lr.fY); |
+ |
+ return SkRect::MakeLTRB(r.fLeft, r.fTop + maxT, r.fRight, r.fBottom - maxB); |
+ |
+} |
+ |
+// The tallest inset rect |
+static SkRect compute_tallest_occluder(const SkRRect& rr) { |
+ SkRect r = rr.getBounds(); |
+ |
+ const SkVector& ul = rr.radii(SkRRect::kUpperLeft_Corner); |
+ const SkVector& ur = rr.radii(SkRRect::kUpperRight_Corner); |
+ const SkVector& lr = rr.radii(SkRRect::kLowerRight_Corner); |
+ const SkVector& ll = rr.radii(SkRRect::kLowerLeft_Corner); |
+ |
+ SkScalar maxL = SkTMax(ul.fX, ll.fX); |
+ SkScalar maxR = SkTMax(ur.fX, lr.fX); |
+ |
+ return SkRect::MakeLTRB(r.fLeft + maxL, r.fTop, r.fRight - maxR, r.fBottom); |
+} |
+ |
+static void draw_rrect(SkCanvas* canvas, const SkRRect& rr, const SkRRect& occRR) { |
+ const SkScalar kBlurSigma = 5.0f; |
+ |
+ SkRect occRect; |
+ SkColor strokeColor; |
+ |
+ { |
+ SkRect occRect1 = compute_central_occluder(occRR); |
+ SkRect occRect2 = compute_widest_occluder(occRR); |
+ SkRect occRect3 = compute_tallest_occluder(occRR); |
+ |
+ SkScalar area1 = occRect1.width() * occRect1.height(); |
+ SkScalar area2 = occRect2.width() * occRect2.height(); |
+ SkScalar area3 = occRect3.width() * occRect3.height(); |
+ |
+ if (area1 >= area2 && area1 >= area3) { |
+ strokeColor = SK_ColorRED; |
+ occRect = occRect1; |
+ } else if (area2 > area3) { |
+ strokeColor = SK_ColorYELLOW; |
+ occRect = occRect2; |
+ } else { |
+ strokeColor = SK_ColorCYAN; |
+ occRect = occRect3; |
+ } |
+ } |
+ |
+ // draw the blur |
+ SkPaint paint; |
+ paint.setMaskFilter(SkBlurMaskFilter::Make(kNormal_SkBlurStyle, kBlurSigma, occRect)); |
+ canvas->drawRRect(rr, paint); |
+ |
+ // draw the stroked geometry of the full occluder |
+ SkPaint stroke; |
+ stroke.setStyle(SkPaint::kStroke_Style); |
+ stroke.setColor(SK_ColorBLUE); |
+ canvas->drawRRect(occRR, stroke); |
+ |
+ // draw the geometry of the occluding rect |
+ stroke.setColor(strokeColor); |
+ canvas->drawRect(occRect, stroke); |
+} |
+ |
+static void draw_45(SkCanvas* canvas, SkRRect::Corner corner, |
+ SkScalar dist, const SkPoint& center) { |
+ SkRRect::Corner left, right; |
+ SkVector dir; |
+ |
+ static const SkScalar kSize = 64.0f / SK_ScalarSqrt2; |
+ |
+ switch (corner) { |
+ case SkRRect::kUpperLeft_Corner: |
+ left = SkRRect::kUpperRight_Corner; |
+ right = SkRRect::kLowerLeft_Corner; |
+ |
+ dir.set(-SK_ScalarRoot2Over2, -SK_ScalarRoot2Over2); |
+ break; |
+ case SkRRect::kUpperRight_Corner: |
+ left = SkRRect::kUpperLeft_Corner; |
+ right = SkRRect::kLowerRight_Corner; |
+ dir.set(SK_ScalarRoot2Over2, -SK_ScalarRoot2Over2); |
+ break; |
+ case SkRRect::kLowerRight_Corner: |
+ left = SkRRect::kLowerLeft_Corner; |
+ right = SkRRect::kUpperRight_Corner; |
+ dir.set(SK_ScalarRoot2Over2, SK_ScalarRoot2Over2); |
+ break; |
+ case SkRRect::kLowerLeft_Corner: |
+ left = SkRRect::kLowerRight_Corner; |
+ right = SkRRect::kUpperLeft_Corner; |
+ dir.set(-SK_ScalarRoot2Over2, SK_ScalarRoot2Over2); |
+ break; |
+ } |
+ |
+ SkRect r = SkRect::MakeWH(kSize, kSize); |
+ // UL, UR, LR, LL |
+ SkVector radii[4] = { { 0.0f, 0.0f }, { 0.0f, 0.0f }, { 0.0f, 0.0f }, { 0.0f, 0.0f } }; |
+ radii[left] = SkVector::Make(kSize, kSize); |
+ radii[right] = SkVector::Make(kSize, kSize); |
+ SkRRect rr; |
+ rr.setRectRadii( |
+ offset_center_to(r.roundOut(), center.fX + dist*dir.fX, center.fY + dist*dir.fY), |
+ radii); |
+ |
+ SkRRect occRR; |
+ dist -= 10.0f; |
+ occRR.setRectRadii( |
+ offset_center_to(r.roundOut(), center.fX + dist*dir.fX, center.fY + dist*dir.fY), |
+ radii); |
+ |
+ draw_rrect(canvas, rr, occRR); |
+} |
+ |
+static void draw_45_simple(SkCanvas* canvas, const SkVector& v, |
+ SkScalar dist, const SkPoint& center) { |
+ SkIRect r = SkIRect::MakeWH(64, 64); |
+ SkRRect rr = SkRRect::MakeRectXY( |
+ offset_center_to(r, center.fX + dist*v.fX, center.fY + dist*v.fY), |
+ 8, 8); |
+ |
+ dist -= 10.0f; |
+ SkRRect occRR = SkRRect::MakeRectXY( |
+ offset_center_to(r, center.fX + dist*v.fX, center.fY + dist*v.fY), |
+ 8, 8); |
+ |
+ draw_rrect(canvas, rr, occRR); |
+} |
+ |
+static void draw_90(SkCanvas* canvas, const SkVector& v, SkScalar dist, const SkPoint& center) { |
+ static const int kWidth = 25; |
+ |
+ SkIRect r; |
+ if (fabs(v.fX) < fabs(v.fY)) { |
+ r = SkIRect::MakeWH(kWidth, 64); |
+ } else { |
+ r = SkIRect::MakeWH(64, kWidth); |
+ } |
+ SkRRect rr = SkRRect::MakeOval( |
+ offset_center_to(r, center.fX + dist*v.fX, center.fY + dist*v.fY)); |
+ |
+ dist -= 10.0f; |
+ SkRRect occRR = SkRRect::MakeOval( |
+ offset_center_to(r, center.fX + dist*v.fX, center.fY + dist*v.fY)); |
+ |
+ draw_rrect(canvas, rr, occRR); |
+} |
+ |
+static void draw_90_simple(SkCanvas* canvas, const SkVector& v, |
+ SkScalar dist, const SkPoint& center) { |
+ static const int kLength = 128; |
+ static const int kWidth = 32; |
+ |
+ SkIRect r; |
+ if (fabs(v.fX) < fabs(v.fY)) { |
+ r = SkIRect::MakeWH(kLength, kWidth); |
+ } else { |
+ r = SkIRect::MakeWH(kWidth, kLength); |
+ } |
+ SkRRect rr = SkRRect::MakeRectXY( |
+ offset_center_to(r, center.fX + dist*v.fX, center.fY + dist*v.fY), |
+ 8, 8); |
+ |
+ dist -= 10.0f; |
+ SkRRect occRR = SkRRect::MakeRectXY( |
+ offset_center_to(r, center.fX + dist*v.fX, center.fY + dist*v.fY), |
+ 8, 8); |
+ |
+ draw_rrect(canvas, rr, occRR); |
+} |
+ |
+ |
+static void draw_30_60(SkCanvas* canvas, int foo, const SkVector& v, |
+ SkScalar dist, const SkPoint& center) { |
+ SkRRect::Corner left, right; |
+ |
+ static const int kLength = 64; |
+ static const int kWidth = 30; |
+ |
+ switch (foo) { |
+ case 0: |
+ left = SkRRect::kUpperRight_Corner; |
+ right = SkRRect::kLowerLeft_Corner; |
+ break; |
+ case 1: |
+ left = SkRRect::kUpperLeft_Corner; |
+ right = SkRRect::kLowerRight_Corner; |
+ break; |
+ case 2: |
+ left = SkRRect::kLowerLeft_Corner; |
+ right = SkRRect::kUpperRight_Corner; |
+ break; |
+ case 3: |
+ left = SkRRect::kLowerRight_Corner; |
+ right = SkRRect::kUpperLeft_Corner; |
+ break; |
+ } |
+ |
+ SkIRect r; |
+ if (fabs(v.fX) < fabs(v.fY)) { |
+ r = SkIRect::MakeWH(kLength, kWidth); |
+ } else { |
+ r = SkIRect::MakeWH(kWidth, kLength); |
+ } |
+ // UL, UR, LR, LL |
+ SkVector radii[4] = { { 0.0f, 0.0f }, { 0.0f, 0.0f }, { 0.0f, 0.0f }, { 0.0f, 0.0f } }; |
+ radii[left] = SkVector::Make(SkIntToScalar(kWidth), SkIntToScalar(kWidth)); |
+ radii[right] = SkVector::Make(SkIntToScalar(kWidth), SkIntToScalar(kWidth)); |
+ SkRRect rr; |
+ rr.setRectRadii(offset_center_to(r, center.fX + dist*v.fX, center.fY + dist*v.fY), radii); |
+ |
+ dist -= 10.0f; |
+ SkRRect occRR; |
+ occRR.setRectRadii(offset_center_to(r, center.fX + dist*v.fX, center.fY + dist*v.fY), radii); |
+ draw_rrect(canvas, rr, occRR); |
+} |
+ |
+namespace skiagm { |
+ |
+class OccludedRRectBlurGM : public GM { |
+public: |
+ OccludedRRectBlurGM() { |
+ this->setBGColor(sk_tool_utils::color_to_565(0xFFCCCCCC)); |
+ } |
+ |
+protected: |
+ |
+ SkString onShortName() override { |
+ return SkString("occludedrrectblur"); |
+ } |
+ |
+ SkISize onISize() override { |
+ return SkISize::Make(kWidth, kHeight); |
+ } |
+ |
+ void onDraw(SkCanvas* canvas) override { |
+ const SkPoint center = SkPoint::Make(kWidth/2, kHeight/2); |
+ |
+ // outer-most big RR |
+ { |
+ SkIRect r = SkIRect::MakeWH(420, 420); |
+ SkRRect rr = SkRRect::MakeRectXY(offset_center_to(r, center.fX, center.fY), 64, 64); |
+ draw_rrect(canvas, rr, rr); |
+ |
+#if 1 |
+ // TODO: remove this. Until we actually start skipping the middle draw we need this |
+ // to provide contrast |
+ SkPaint temp; |
+ temp.setColor(sk_tool_utils::color_to_565(0xFFCCCCCC)); |
+ r.inset(32, 32); |
+ canvas->drawRect(offset_center_to(r, center.fX, center.fY), temp); |
+#endif |
+ } |
+ |
+ // center circle |
+ { |
+ SkIRect r = SkIRect::MakeWH(32, 32); |
+ SkRRect rr = SkRRect::MakeOval(offset_center_to(r, center.fX, center.fY)); |
+ draw_rrect(canvas, rr, rr); |
+ } |
+ |
+ draw_45(canvas, SkRRect::kUpperLeft_Corner, 64, center); |
+ draw_45(canvas, SkRRect::kUpperRight_Corner, 64, center); |
+ draw_45(canvas, SkRRect::kLowerRight_Corner, 64, center); |
+ draw_45(canvas, SkRRect::kLowerLeft_Corner, 64, center); |
+ |
+ draw_90(canvas, SkVector::Make(-1.0f, 0.0f), 64, center); |
+ draw_90(canvas, SkVector::Make(0.0f, -1.0f), 64, center); |
+ draw_90(canvas, SkVector::Make(1.0f, 0.0f), 64, center); |
+ draw_90(canvas, SkVector::Make(0.0f, 1.0f), 64, center); |
+ |
+ static const SkScalar kRoot3Over2 = 0.8660254037844386f; |
+ |
+ draw_30_60(canvas, 3, SkVector::Make(0.5f, kRoot3Over2), 120, center); |
+ draw_30_60(canvas, 1, SkVector::Make(kRoot3Over2, 0.5f), 120, center); |
+ |
+ draw_30_60(canvas, 0, SkVector::Make(-0.5f, kRoot3Over2), 120, center); |
+ draw_30_60(canvas, 2, SkVector::Make(-kRoot3Over2, 0.5f), 120, center); |
+ |
+ draw_30_60(canvas, 3, SkVector::Make(-0.5f, -kRoot3Over2), 120, center); |
+ draw_30_60(canvas, 1, SkVector::Make(-kRoot3Over2, -0.5f), 120, center); |
+ |
+ draw_30_60(canvas, 0, SkVector::Make(0.5f, -kRoot3Over2), 120, center); |
+ draw_30_60(canvas, 2, SkVector::Make(kRoot3Over2, -0.5f), 120, center); |
+ |
+ draw_45_simple(canvas, SkVector::Make(-SK_ScalarRoot2Over2, -SK_ScalarRoot2Over2), |
+ 190, center); |
+ draw_45_simple(canvas, SkVector::Make(SK_ScalarRoot2Over2, -SK_ScalarRoot2Over2), |
+ 190, center); |
+ draw_45_simple(canvas, SkVector::Make(SK_ScalarRoot2Over2, SK_ScalarRoot2Over2), |
+ 190, center); |
+ draw_45_simple(canvas, SkVector::Make(-SK_ScalarRoot2Over2, SK_ScalarRoot2Over2), |
+ 190, center); |
+ |
+ draw_90_simple(canvas, SkVector::Make(-1.0f, 0.0f), 150, center); |
+ draw_90_simple(canvas, SkVector::Make(0.0f, -1.0f), 150, center); |
+ draw_90_simple(canvas, SkVector::Make(1.0f, 0.0f), 150, center); |
+ draw_90_simple(canvas, SkVector::Make(0.0f, 1.0f), 150, center); |
+ } |
+ |
+private: |
+ static const int kWidth = 440; |
+ static const int kHeight = 440; |
+ |
+ typedef GM INHERITED; |
+}; |
+ |
+////////////////////////////////////////////////////////////////////////////// |
+ |
+DEF_GM(return new OccludedRRectBlurGM;) |
+} |