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) = default; |
| 82 ShadowDetails::~ShadowDetails() {} |
| 83 |
| 84 const ShadowDetails& ShadowDetails::Get(int elevation, int corner_radius) { |
| 85 auto iter = |
| 86 g_shadow_cache.Get().find(std::make_pair(elevation, corner_radius)); |
| 87 if (iter != g_shadow_cache.Get().end()) |
| 88 return iter->second; |
| 89 |
| 90 auto insertion = g_shadow_cache.Get().insert(std::make_pair( |
| 91 std::make_pair(elevation, corner_radius), ShadowDetails())); |
| 92 DCHECK(insertion.second); |
| 93 ShadowDetails* shadow = &insertion.first->second; |
| 94 // To match the CSS notion of blur (spread outside the bounding box) to the |
| 95 // Skia notion of blur (spread outside and inside the bounding box), we have |
| 96 // to double the designer-provided blur values. |
| 97 const int kBlurCorrection = 2; |
| 98 // "Key shadow": y offset is elevation and blur is twice the elevation. |
| 99 shadow->values.emplace_back(gfx::Vector2d(0, elevation), |
| 100 kBlurCorrection * elevation * 2, |
| 101 SkColorSetA(SK_ColorBLACK, 0x3d)); |
| 102 // "Ambient shadow": no offset and blur matches the elevation. |
| 103 shadow->values.emplace_back(gfx::Vector2d(), kBlurCorrection * elevation, |
| 104 SkColorSetA(SK_ColorBLACK, 0x1f)); |
| 105 // To see what this looks like for elevation 24, try this CSS: |
| 106 // box-shadow: 0 24px 48px rgba(0, 0, 0, .24), |
| 107 // 0 0 24px rgba(0, 0, 0, .12); |
| 108 auto source = new ShadowNineboxSource(shadow->values, corner_radius); |
| 109 shadow->ninebox_image = ImageSkia(source, source->size()); |
| 110 return *shadow; |
| 111 } |
| 112 |
| 113 } // namespace gfx |
OLD | NEW |