OLD | NEW |
(Empty) | |
| 1 /* |
| 2 * Copyright 2016 Google Inc. |
| 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. |
| 6 */ |
| 7 |
| 8 #include "gm.h" |
| 9 #include "SkBlurMaskFilter.h" |
| 10 #include "SkRRect.h" |
| 11 |
| 12 static SkRect offset_center_to(const SkIRect& src, SkScalar x, SkScalar y) { |
| 13 SkScalar halfW = 0.5f * src.width(); |
| 14 SkScalar halfH = 0.5f * src.height(); |
| 15 |
| 16 return SkRect::MakeLTRB(x - halfW, y - halfH, x + halfW, y + halfH); |
| 17 } |
| 18 |
| 19 // compute the intersection point between the diagonal and the ellipse in the |
| 20 // lower right corner |
| 21 static SkPoint intersection(SkScalar w, SkScalar h) { |
| 22 SkASSERT(w > 0.0f || h > 0.0f); |
| 23 |
| 24 return SkPoint::Make(w / SK_ScalarSqrt2, h / SK_ScalarSqrt2); |
| 25 } |
| 26 |
| 27 // Use the intersection of the corners' diagonals with their ellipses to shrink |
| 28 // the bounding rect |
| 29 static SkRect compute_central_occluder(const SkRRect& rr) { |
| 30 const SkRect r = rr.getBounds(); |
| 31 |
| 32 SkScalar newL = r.fLeft, newT = r.fTop, newR = r.fRight, newB = r.fBottom; |
| 33 |
| 34 SkVector radii = rr.radii(SkRRect::kUpperLeft_Corner); |
| 35 if (!radii.isZero()) { |
| 36 SkPoint p = intersection(radii.fX, radii.fY); |
| 37 |
| 38 newL = SkTMax(newL, r.fLeft + radii.fX - p.fX); |
| 39 newT = SkTMax(newT, r.fTop + radii.fY - p.fY); |
| 40 } |
| 41 |
| 42 radii = rr.radii(SkRRect::kUpperRight_Corner); |
| 43 if (!radii.isZero()) { |
| 44 SkPoint p = intersection(radii.fX, radii.fY); |
| 45 |
| 46 newR = SkTMin(newR, r.fRight + p.fX - radii.fX); |
| 47 newT = SkTMax(newT, r.fTop + radii.fY - p.fY); |
| 48 } |
| 49 |
| 50 radii = rr.radii(SkRRect::kLowerRight_Corner); |
| 51 if (!radii.isZero()) { |
| 52 SkPoint p = intersection(radii.fX, radii.fY); |
| 53 |
| 54 newR = SkTMin(newR, r.fRight + p.fX - radii.fX); |
| 55 newB = SkTMin(newB, r.fBottom - radii.fY + p.fY); |
| 56 } |
| 57 |
| 58 radii = rr.radii(SkRRect::kLowerLeft_Corner); |
| 59 if (!radii.isZero()) { |
| 60 SkPoint p = intersection(radii.fX, radii.fY); |
| 61 |
| 62 newL = SkTMax(newL, r.fLeft + radii.fX - p.fX); |
| 63 newB = SkTMin(newB, r.fBottom - radii.fY + p.fY); |
| 64 } |
| 65 |
| 66 return SkRect::MakeLTRB(newL, newT, newR, newB); |
| 67 } |
| 68 |
| 69 // The widest inset rect |
| 70 static SkRect compute_widest_occluder(const SkRRect& rr) { |
| 71 SkRect r = rr.getBounds(); |
| 72 |
| 73 const SkVector& ul = rr.radii(SkRRect::kUpperLeft_Corner); |
| 74 const SkVector& ur = rr.radii(SkRRect::kUpperRight_Corner); |
| 75 const SkVector& lr = rr.radii(SkRRect::kLowerRight_Corner); |
| 76 const SkVector& ll = rr.radii(SkRRect::kLowerLeft_Corner); |
| 77 |
| 78 SkScalar maxT = SkTMax(ul.fY, ur.fY); |
| 79 SkScalar maxB = SkTMax(ll.fY, lr.fY); |
| 80 |
| 81 return SkRect::MakeLTRB(r.fLeft, r.fTop + maxT, r.fRight, r.fBottom - maxB); |
| 82 |
| 83 } |
| 84 |
| 85 // The tallest inset rect |
| 86 static SkRect compute_tallest_occluder(const SkRRect& rr) { |
| 87 SkRect r = rr.getBounds(); |
| 88 |
| 89 const SkVector& ul = rr.radii(SkRRect::kUpperLeft_Corner); |
| 90 const SkVector& ur = rr.radii(SkRRect::kUpperRight_Corner); |
| 91 const SkVector& lr = rr.radii(SkRRect::kLowerRight_Corner); |
| 92 const SkVector& ll = rr.radii(SkRRect::kLowerLeft_Corner); |
| 93 |
| 94 SkScalar maxL = SkTMax(ul.fX, ll.fX); |
| 95 SkScalar maxR = SkTMax(ur.fX, lr.fX); |
| 96 |
| 97 return SkRect::MakeLTRB(r.fLeft + maxL, r.fTop, r.fRight - maxR, r.fBottom); |
| 98 } |
| 99 |
| 100 static void draw_rrect(SkCanvas* canvas, const SkRRect& rr, const SkRRect& occRR
) { |
| 101 const SkScalar kBlurSigma = 5.0f; |
| 102 |
| 103 SkRect occRect; |
| 104 SkColor strokeColor; |
| 105 |
| 106 { |
| 107 SkRect occRect1 = compute_central_occluder(occRR); |
| 108 SkRect occRect2 = compute_widest_occluder(occRR); |
| 109 SkRect occRect3 = compute_tallest_occluder(occRR); |
| 110 |
| 111 SkScalar area1 = occRect1.width() * occRect1.height(); |
| 112 SkScalar area2 = occRect2.width() * occRect2.height(); |
| 113 SkScalar area3 = occRect3.width() * occRect3.height(); |
| 114 |
| 115 if (area1 >= area2 && area1 >= area3) { |
| 116 strokeColor = SK_ColorRED; |
| 117 occRect = occRect1; |
| 118 } else if (area2 > area3) { |
| 119 strokeColor = SK_ColorYELLOW; |
| 120 occRect = occRect2; |
| 121 } else { |
| 122 strokeColor = SK_ColorCYAN; |
| 123 occRect = occRect3; |
| 124 } |
| 125 } |
| 126 |
| 127 // draw the blur |
| 128 SkPaint paint; |
| 129 paint.setMaskFilter(SkBlurMaskFilter::Make(kNormal_SkBlurStyle, kBlurSigma,
occRect)); |
| 130 canvas->drawRRect(rr, paint); |
| 131 |
| 132 // draw the stroked geometry of the full occluder |
| 133 SkPaint stroke; |
| 134 stroke.setStyle(SkPaint::kStroke_Style); |
| 135 stroke.setColor(SK_ColorBLUE); |
| 136 canvas->drawRRect(occRR, stroke); |
| 137 |
| 138 // draw the geometry of the occluding rect |
| 139 stroke.setColor(strokeColor); |
| 140 canvas->drawRect(occRect, stroke); |
| 141 } |
| 142 |
| 143 static void draw_45(SkCanvas* canvas, SkRRect::Corner corner, |
| 144 SkScalar dist, const SkPoint& center) { |
| 145 SkRRect::Corner left, right; |
| 146 SkVector dir; |
| 147 |
| 148 static const SkScalar kSize = 64.0f / SK_ScalarSqrt2; |
| 149 |
| 150 switch (corner) { |
| 151 case SkRRect::kUpperLeft_Corner: |
| 152 left = SkRRect::kUpperRight_Corner; |
| 153 right = SkRRect::kLowerLeft_Corner; |
| 154 |
| 155 dir.set(-SK_ScalarRoot2Over2, -SK_ScalarRoot2Over2); |
| 156 break; |
| 157 case SkRRect::kUpperRight_Corner: |
| 158 left = SkRRect::kUpperLeft_Corner; |
| 159 right = SkRRect::kLowerRight_Corner; |
| 160 dir.set(SK_ScalarRoot2Over2, -SK_ScalarRoot2Over2); |
| 161 break; |
| 162 case SkRRect::kLowerRight_Corner: |
| 163 left = SkRRect::kLowerLeft_Corner; |
| 164 right = SkRRect::kUpperRight_Corner; |
| 165 dir.set(SK_ScalarRoot2Over2, SK_ScalarRoot2Over2); |
| 166 break; |
| 167 case SkRRect::kLowerLeft_Corner: |
| 168 left = SkRRect::kLowerRight_Corner; |
| 169 right = SkRRect::kUpperLeft_Corner; |
| 170 dir.set(-SK_ScalarRoot2Over2, SK_ScalarRoot2Over2); |
| 171 break; |
| 172 } |
| 173 |
| 174 SkRect r = SkRect::MakeWH(kSize, kSize); |
| 175 // UL, UR, LR, LL |
| 176 SkVector radii[4] = { { 0.0f, 0.0f }, { 0.0f, 0.0f }, { 0.0f, 0.0f }, { 0.0f
, 0.0f } }; |
| 177 radii[left] = SkVector::Make(kSize, kSize); |
| 178 radii[right] = SkVector::Make(kSize, kSize); |
| 179 SkRRect rr; |
| 180 rr.setRectRadii( |
| 181 offset_center_to(r.roundOut(), center.fX + dist*dir.fX, center.fY +
dist*dir.fY), |
| 182 radii); |
| 183 |
| 184 SkRRect occRR; |
| 185 dist -= 10.0f; |
| 186 occRR.setRectRadii( |
| 187 offset_center_to(r.roundOut(), center.fX + dist*dir.fX, center.fY +
dist*dir.fY), |
| 188 radii); |
| 189 |
| 190 draw_rrect(canvas, rr, occRR); |
| 191 } |
| 192 |
| 193 static void draw_45_simple(SkCanvas* canvas, const SkVector& v, |
| 194 SkScalar dist, const SkPoint& center) { |
| 195 SkIRect r = SkIRect::MakeWH(64, 64); |
| 196 SkRRect rr = SkRRect::MakeRectXY( |
| 197 offset_center_to(r, center.fX + dist*v.fX, center.fY
+ dist*v.fY), |
| 198 8, 8); |
| 199 |
| 200 dist -= 10.0f; |
| 201 SkRRect occRR = SkRRect::MakeRectXY( |
| 202 offset_center_to(r, center.fX + dist*v.fX, center.fY
+ dist*v.fY), |
| 203 8, 8); |
| 204 |
| 205 draw_rrect(canvas, rr, occRR); |
| 206 } |
| 207 |
| 208 static void draw_90(SkCanvas* canvas, const SkVector& v, SkScalar dist, const Sk
Point& center) { |
| 209 static const int kWidth = 25; |
| 210 |
| 211 SkIRect r; |
| 212 if (fabs(v.fX) < fabs(v.fY)) { |
| 213 r = SkIRect::MakeWH(kWidth, 64); |
| 214 } else { |
| 215 r = SkIRect::MakeWH(64, kWidth); |
| 216 } |
| 217 SkRRect rr = SkRRect::MakeOval( |
| 218 offset_center_to(r, center.fX + dist*v.fX, center.fY
+ dist*v.fY)); |
| 219 |
| 220 dist -= 10.0f; |
| 221 SkRRect occRR = SkRRect::MakeOval( |
| 222 offset_center_to(r, center.fX + dist*v.fX, center.fY
+ dist*v.fY)); |
| 223 |
| 224 draw_rrect(canvas, rr, occRR); |
| 225 } |
| 226 |
| 227 static void draw_90_simple(SkCanvas* canvas, const SkVector& v, |
| 228 SkScalar dist, const SkPoint& center) { |
| 229 static const int kLength = 128; |
| 230 static const int kWidth = 32; |
| 231 |
| 232 SkIRect r; |
| 233 if (fabs(v.fX) < fabs(v.fY)) { |
| 234 r = SkIRect::MakeWH(kLength, kWidth); |
| 235 } else { |
| 236 r = SkIRect::MakeWH(kWidth, kLength); |
| 237 } |
| 238 SkRRect rr = SkRRect::MakeRectXY( |
| 239 offset_center_to(r, center.fX + dist*v.fX, center.fY
+ dist*v.fY), |
| 240 8, 8); |
| 241 |
| 242 dist -= 10.0f; |
| 243 SkRRect occRR = SkRRect::MakeRectXY( |
| 244 offset_center_to(r, center.fX + dist*v.fX, center.fY
+ dist*v.fY), |
| 245 8, 8); |
| 246 |
| 247 draw_rrect(canvas, rr, occRR); |
| 248 } |
| 249 |
| 250 |
| 251 static void draw_30_60(SkCanvas* canvas, int foo, const SkVector& v, |
| 252 SkScalar dist, const SkPoint& center) { |
| 253 SkRRect::Corner left, right; |
| 254 |
| 255 static const int kLength = 64; |
| 256 static const int kWidth = 30; |
| 257 |
| 258 switch (foo) { |
| 259 case 0: |
| 260 left = SkRRect::kUpperRight_Corner; |
| 261 right = SkRRect::kLowerLeft_Corner; |
| 262 break; |
| 263 case 1: |
| 264 left = SkRRect::kUpperLeft_Corner; |
| 265 right = SkRRect::kLowerRight_Corner; |
| 266 break; |
| 267 case 2: |
| 268 left = SkRRect::kLowerLeft_Corner; |
| 269 right = SkRRect::kUpperRight_Corner; |
| 270 break; |
| 271 case 3: |
| 272 left = SkRRect::kLowerRight_Corner; |
| 273 right = SkRRect::kUpperLeft_Corner; |
| 274 break; |
| 275 } |
| 276 |
| 277 SkIRect r; |
| 278 if (fabs(v.fX) < fabs(v.fY)) { |
| 279 r = SkIRect::MakeWH(kLength, kWidth); |
| 280 } else { |
| 281 r = SkIRect::MakeWH(kWidth, kLength); |
| 282 } |
| 283 // UL, UR, LR, LL |
| 284 SkVector radii[4] = { { 0.0f, 0.0f }, { 0.0f, 0.0f }, { 0.0f, 0.0f }, { 0.0f
, 0.0f } }; |
| 285 radii[left] = SkVector::Make(SkIntToScalar(kWidth), SkIntToScalar(kWidth)); |
| 286 radii[right] = SkVector::Make(SkIntToScalar(kWidth), SkIntToScalar(kWidth)); |
| 287 SkRRect rr; |
| 288 rr.setRectRadii(offset_center_to(r, center.fX + dist*v.fX, center.fY + dist*
v.fY), radii); |
| 289 |
| 290 dist -= 10.0f; |
| 291 SkRRect occRR; |
| 292 occRR.setRectRadii(offset_center_to(r, center.fX + dist*v.fX, center.fY + di
st*v.fY), radii); |
| 293 draw_rrect(canvas, rr, occRR); |
| 294 } |
| 295 |
| 296 namespace skiagm { |
| 297 |
| 298 class OccludedRRectBlurGM : public GM { |
| 299 public: |
| 300 OccludedRRectBlurGM() { |
| 301 this->setBGColor(sk_tool_utils::color_to_565(0xFFCCCCCC)); |
| 302 } |
| 303 |
| 304 protected: |
| 305 |
| 306 SkString onShortName() override { |
| 307 return SkString("occludedrrectblur"); |
| 308 } |
| 309 |
| 310 SkISize onISize() override { |
| 311 return SkISize::Make(kWidth, kHeight); |
| 312 } |
| 313 |
| 314 void onDraw(SkCanvas* canvas) override { |
| 315 const SkPoint center = SkPoint::Make(kWidth/2, kHeight/2); |
| 316 |
| 317 // outer-most big RR |
| 318 { |
| 319 SkIRect r = SkIRect::MakeWH(420, 420); |
| 320 SkRRect rr = SkRRect::MakeRectXY(offset_center_to(r, center.fX, cent
er.fY), 64, 64); |
| 321 draw_rrect(canvas, rr, rr); |
| 322 |
| 323 #if 1 |
| 324 // TODO: remove this. Until we actually start skipping the middle dr
aw we need this |
| 325 // to provide contrast |
| 326 SkPaint temp; |
| 327 temp.setColor(sk_tool_utils::color_to_565(0xFFCCCCCC)); |
| 328 r.inset(32, 32); |
| 329 canvas->drawRect(offset_center_to(r, center.fX, center.fY), temp); |
| 330 #endif |
| 331 } |
| 332 |
| 333 // center circle |
| 334 { |
| 335 SkIRect r = SkIRect::MakeWH(32, 32); |
| 336 SkRRect rr = SkRRect::MakeOval(offset_center_to(r, center.fX, center
.fY)); |
| 337 draw_rrect(canvas, rr, rr); |
| 338 } |
| 339 |
| 340 draw_45(canvas, SkRRect::kUpperLeft_Corner, 64, center); |
| 341 draw_45(canvas, SkRRect::kUpperRight_Corner, 64, center); |
| 342 draw_45(canvas, SkRRect::kLowerRight_Corner, 64, center); |
| 343 draw_45(canvas, SkRRect::kLowerLeft_Corner, 64, center); |
| 344 |
| 345 draw_90(canvas, SkVector::Make(-1.0f, 0.0f), 64, center); |
| 346 draw_90(canvas, SkVector::Make(0.0f, -1.0f), 64, center); |
| 347 draw_90(canvas, SkVector::Make(1.0f, 0.0f), 64, center); |
| 348 draw_90(canvas, SkVector::Make(0.0f, 1.0f), 64, center); |
| 349 |
| 350 static const SkScalar kRoot3Over2 = 0.8660254037844386f; |
| 351 |
| 352 draw_30_60(canvas, 3, SkVector::Make(0.5f, kRoot3Over2), 120, center); |
| 353 draw_30_60(canvas, 1, SkVector::Make(kRoot3Over2, 0.5f), 120, center); |
| 354 |
| 355 draw_30_60(canvas, 0, SkVector::Make(-0.5f, kRoot3Over2), 120, center); |
| 356 draw_30_60(canvas, 2, SkVector::Make(-kRoot3Over2, 0.5f), 120, center); |
| 357 |
| 358 draw_30_60(canvas, 3, SkVector::Make(-0.5f, -kRoot3Over2), 120, center); |
| 359 draw_30_60(canvas, 1, SkVector::Make(-kRoot3Over2, -0.5f), 120, center); |
| 360 |
| 361 draw_30_60(canvas, 0, SkVector::Make(0.5f, -kRoot3Over2), 120, center); |
| 362 draw_30_60(canvas, 2, SkVector::Make(kRoot3Over2, -0.5f), 120, center); |
| 363 |
| 364 draw_45_simple(canvas, SkVector::Make(-SK_ScalarRoot2Over2, -SK_ScalarRo
ot2Over2), |
| 365 190, center); |
| 366 draw_45_simple(canvas, SkVector::Make(SK_ScalarRoot2Over2, -SK_ScalarRoo
t2Over2), |
| 367 190, center); |
| 368 draw_45_simple(canvas, SkVector::Make(SK_ScalarRoot2Over2, SK_ScalarRoot
2Over2), |
| 369 190, center); |
| 370 draw_45_simple(canvas, SkVector::Make(-SK_ScalarRoot2Over2, SK_ScalarRoo
t2Over2), |
| 371 190, center); |
| 372 |
| 373 draw_90_simple(canvas, SkVector::Make(-1.0f, 0.0f), 150, center); |
| 374 draw_90_simple(canvas, SkVector::Make(0.0f, -1.0f), 150, center); |
| 375 draw_90_simple(canvas, SkVector::Make(1.0f, 0.0f), 150, center); |
| 376 draw_90_simple(canvas, SkVector::Make(0.0f, 1.0f), 150, center); |
| 377 } |
| 378 |
| 379 private: |
| 380 static const int kWidth = 440; |
| 381 static const int kHeight = 440; |
| 382 |
| 383 typedef GM INHERITED; |
| 384 }; |
| 385 |
| 386 ////////////////////////////////////////////////////////////////////////////// |
| 387 |
| 388 DEF_GM(return new OccludedRRectBlurGM;) |
| 389 } |
OLD | NEW |