OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "ui/gfx/shadow_util.h" | |
6 | |
7 #include <map> | |
8 #include <vector> | |
9 | |
10 #include "base/lazy_instance.h" | |
11 #include "third_party/skia/include/core/SkDrawLooper.h" | |
12 #include "third_party/skia/include/core/SkRRect.h" | |
13 #include "ui/gfx/canvas.h" | |
14 #include "ui/gfx/geometry/insets.h" | |
15 #include "ui/gfx/image/canvas_image_source.h" | |
16 #include "ui/gfx/shadow_value.h" | |
17 #include "ui/gfx/skia_util.h" | |
18 | |
19 namespace gfx { | |
20 namespace { | |
21 | |
22 // Creates an image with the given shadows painted around a round rect with | |
23 // the given corner radius. The image will be just large enough to paint the | |
24 // shadows appropriately with a 1px square region reserved for "content". | |
25 class ShadowNineboxSource : public CanvasImageSource { | |
26 public: | |
27 ShadowNineboxSource(const std::vector<ShadowValue>& shadows, | |
28 float corner_radius) | |
29 : CanvasImageSource(CalculateSize(shadows, corner_radius), false), | |
30 shadows_(shadows), | |
31 corner_radius_(corner_radius) { | |
32 DCHECK(!shadows.empty()); | |
33 } | |
34 ~ShadowNineboxSource() override {} | |
35 | |
36 // CanvasImageSource overrides: | |
37 void Draw(Canvas* canvas) override { | |
38 SkPaint paint; | |
39 paint.setLooper(CreateShadowDrawLooperCorrectBlur(shadows_)); | |
40 Insets insets = -ShadowValue::GetMargin(shadows_); | |
41 gfx::Rect bounds(size()); | |
42 bounds.Inset(insets); | |
43 SkRRect r_rect = SkRRect::MakeRectXY(gfx::RectToSkRect(bounds), | |
44 corner_radius_, corner_radius_); | |
45 | |
46 // Clip out the center so it's not painted with the shadow. | |
47 canvas->sk_canvas()->clipRRect(r_rect, SkClipOp::kDifference, true); | |
48 // Clipping alone is not enough --- due to anti aliasing there will still be | |
49 // some of the fill color in the rounded corners. We must make the fill | |
50 // color transparent. | |
51 paint.setColor(SK_ColorTRANSPARENT); | |
52 canvas->sk_canvas()->drawRRect(r_rect, paint); | |
53 } | |
54 | |
55 private: | |
56 static Size CalculateSize(const std::vector<ShadowValue>& shadows, | |
57 float corner_radius) { | |
58 // The "content" area (the middle tile in the 3x3 grid) is a single pixel. | |
59 gfx::Rect bounds(0, 0, 1, 1); | |
60 // We need enough space to render the full range of blur. | |
61 bounds.Inset(-ShadowValue::GetBlurRegion(shadows)); | |
62 // We also need space for the full roundrect corner rounding. | |
63 bounds.Inset(-gfx::Insets(corner_radius)); | |
64 return bounds.size(); | |
65 } | |
66 | |
67 const std::vector<ShadowValue> shadows_; | |
68 | |
69 const float corner_radius_; | |
70 | |
71 DISALLOW_COPY_AND_ASSIGN(ShadowNineboxSource); | |
72 }; | |
73 | |
74 // Map from elevation/corner radius pair to a cached shadow. | |
75 using ShadowDetailsMap = std::map<std::pair<int, int>, ShadowDetails>; | |
76 base::LazyInstance<ShadowDetailsMap> g_shadow_cache = LAZY_INSTANCE_INITIALIZER; | |
77 | |
78 } // namespace | |
79 | |
80 ShadowDetails::ShadowDetails() {} | |
81 ShadowDetails::ShadowDetails(const ShadowDetails& other) { | |
sky
2017/01/05 00:39:17
Does = default; work here?
Evan Stade
2017/01/05 00:51:15
ah, that's nifty. Seems to work.
| |
82 values = other.values; | |
83 ninebox_image = other.ninebox_image; | |
84 } | |
85 ShadowDetails::~ShadowDetails() {} | |
86 | |
87 const ShadowDetails& ShadowDetails::Get(int elevation, int corner_radius) { | |
88 auto iter = | |
89 g_shadow_cache.Get().find(std::make_pair(elevation, corner_radius)); | |
90 if (iter != g_shadow_cache.Get().end()) | |
91 return iter->second; | |
92 | |
93 auto insertion = g_shadow_cache.Get().insert(std::make_pair( | |
94 std::make_pair(elevation, corner_radius), ShadowDetails())); | |
95 DCHECK(insertion.second); | |
96 ShadowDetails* shadow = &insertion.first->second; | |
97 // To match the CSS notion of blur (spread outside the bounding box) to the | |
98 // Skia notion of blur (spread outside and inside the bounding box), we have | |
99 // to double the designer-provided blur values. | |
100 const int kBlurCorrection = 2; | |
101 // "Key shadow": y offset is elevation and blur is twice the elevation. | |
102 shadow->values.emplace_back(gfx::Vector2d(0, elevation), | |
103 kBlurCorrection * elevation * 2, | |
104 SkColorSetA(SK_ColorBLACK, 0x3d)); | |
105 // "Ambient shadow": no offset and blur matches the elevation. | |
106 shadow->values.emplace_back(gfx::Vector2d(), kBlurCorrection * elevation, | |
107 SkColorSetA(SK_ColorBLACK, 0x1f)); | |
108 // To see what this looks like for elevation 24, try this CSS: | |
109 // box-shadow: 0 24px 48px rgba(0, 0, 0, .24), | |
110 // 0 0 24px rgba(0, 0, 0, .12); | |
111 auto source = new ShadowNineboxSource(shadow->values, corner_radius); | |
112 shadow->ninebox_image = ImageSkia(source, source->size()); | |
113 return *shadow; | |
114 } | |
115 | |
116 } // namespace gfx | |
OLD | NEW |