Chromium Code Reviews| 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 |