estade | 36f01b0 | 2017-01-05 01:49:08 | [diff] [blame] | 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" |
Daniel Cheng | 23eed1d | 2017-08-18 02:42:31 | [diff] [blame] | 11 | #include "base/memory/ptr_util.h" |
estade | 36f01b0 | 2017-01-05 01:49:08 | [diff] [blame] | 12 | #include "third_party/skia/include/core/SkDrawLooper.h" |
| 13 | #include "third_party/skia/include/core/SkRRect.h" |
| 14 | #include "ui/gfx/canvas.h" |
| 15 | #include "ui/gfx/geometry/insets.h" |
| 16 | #include "ui/gfx/image/canvas_image_source.h" |
| 17 | #include "ui/gfx/shadow_value.h" |
enne | eb8c638a | 2017-01-31 23:29:56 | [diff] [blame] | 18 | #include "ui/gfx/skia_paint_util.h" |
estade | 36f01b0 | 2017-01-05 01:49:08 | [diff] [blame] | 19 | #include "ui/gfx/skia_util.h" |
| 20 | |
| 21 | namespace gfx { |
| 22 | namespace { |
| 23 | |
| 24 | // Creates an image with the given shadows painted around a round rect with |
| 25 | // the given corner radius. The image will be just large enough to paint the |
| 26 | // shadows appropriately with a 1px square region reserved for "content". |
| 27 | class ShadowNineboxSource : public CanvasImageSource { |
| 28 | public: |
| 29 | ShadowNineboxSource(const std::vector<ShadowValue>& shadows, |
| 30 | float corner_radius) |
Peter Kasting | 0aa1854 | 2019-06-22 05:35:01 | [diff] [blame] | 31 | : CanvasImageSource(CalculateSize(shadows, corner_radius)), |
estade | 36f01b0 | 2017-01-05 01:49:08 | [diff] [blame] | 32 | shadows_(shadows), |
| 33 | corner_radius_(corner_radius) { |
| 34 | DCHECK(!shadows.empty()); |
| 35 | } |
| 36 | ~ShadowNineboxSource() override {} |
| 37 | |
| 38 | // CanvasImageSource overrides: |
| 39 | void Draw(Canvas* canvas) override { |
enne | aae3c56 | 2017-02-28 04:26:45 | [diff] [blame] | 40 | cc::PaintFlags flags; |
estade | 9ef3c96 | 2017-03-09 05:16:59 | [diff] [blame] | 41 | flags.setLooper(CreateShadowDrawLooper(shadows_)); |
estade | 36f01b0 | 2017-01-05 01:49:08 | [diff] [blame] | 42 | Insets insets = -ShadowValue::GetMargin(shadows_); |
| 43 | gfx::Rect bounds(size()); |
| 44 | bounds.Inset(insets); |
| 45 | SkRRect r_rect = SkRRect::MakeRectXY(gfx::RectToSkRect(bounds), |
| 46 | corner_radius_, corner_radius_); |
| 47 | |
| 48 | // Clip out the center so it's not painted with the shadow. |
| 49 | canvas->sk_canvas()->clipRRect(r_rect, SkClipOp::kDifference, true); |
| 50 | // Clipping alone is not enough --- due to anti aliasing there will still be |
| 51 | // some of the fill color in the rounded corners. We must make the fill |
| 52 | // color transparent. |
enne | aae3c56 | 2017-02-28 04:26:45 | [diff] [blame] | 53 | flags.setColor(SK_ColorTRANSPARENT); |
| 54 | canvas->sk_canvas()->drawRRect(r_rect, flags); |
estade | 36f01b0 | 2017-01-05 01:49:08 | [diff] [blame] | 55 | } |
| 56 | |
| 57 | private: |
| 58 | static Size CalculateSize(const std::vector<ShadowValue>& shadows, |
| 59 | float corner_radius) { |
| 60 | // The "content" area (the middle tile in the 3x3 grid) is a single pixel. |
| 61 | gfx::Rect bounds(0, 0, 1, 1); |
| 62 | // We need enough space to render the full range of blur. |
| 63 | bounds.Inset(-ShadowValue::GetBlurRegion(shadows)); |
| 64 | // We also need space for the full roundrect corner rounding. |
| 65 | bounds.Inset(-gfx::Insets(corner_radius)); |
| 66 | return bounds.size(); |
| 67 | } |
| 68 | |
| 69 | const std::vector<ShadowValue> shadows_; |
| 70 | |
| 71 | const float corner_radius_; |
| 72 | |
| 73 | DISALLOW_COPY_AND_ASSIGN(ShadowNineboxSource); |
| 74 | }; |
| 75 | |
| 76 | // Map from elevation/corner radius pair to a cached shadow. |
| 77 | using ShadowDetailsMap = std::map<std::pair<int, int>, ShadowDetails>; |
scottmg | 5e65e3a | 2017-03-08 08:48:46 | [diff] [blame] | 78 | base::LazyInstance<ShadowDetailsMap>::DestructorAtExit g_shadow_cache = |
| 79 | LAZY_INSTANCE_INITIALIZER; |
estade | 36f01b0 | 2017-01-05 01:49:08 | [diff] [blame] | 80 | |
| 81 | } // namespace |
| 82 | |
| 83 | ShadowDetails::ShadowDetails() {} |
| 84 | ShadowDetails::ShadowDetails(const ShadowDetails& other) = default; |
| 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 | |
Darwin Huang | 2b91ba7 | 2019-08-20 17:59:20 | [diff] [blame] | 93 | auto insertion = g_shadow_cache.Get().emplace( |
| 94 | std::make_pair(elevation, corner_radius), ShadowDetails()); |
estade | 36f01b0 | 2017-01-05 01:49:08 | [diff] [blame] | 95 | DCHECK(insertion.second); |
| 96 | ShadowDetails* shadow = &insertion.first->second; |
xiaohuic | 08a5748 | 2017-06-21 22:20:05 | [diff] [blame] | 97 | shadow->values = ShadowValue::MakeMdShadowValues(elevation); |
vmpstr | 6d9996c8 | 2017-02-23 00:43:25 | [diff] [blame] | 98 | auto* source = new ShadowNineboxSource(shadow->values, corner_radius); |
Daniel Cheng | 23eed1d | 2017-08-18 02:42:31 | [diff] [blame] | 99 | shadow->ninebox_image = ImageSkia(base::WrapUnique(source), source->size()); |
estade | 36f01b0 | 2017-01-05 01:49:08 | [diff] [blame] | 100 | return *shadow; |
| 101 | } |
| 102 | |
| 103 | } // namespace gfx |