blob: 985d854585251bae6efd49aff81b0f559f2006fc [file] [log] [blame]
estade36f01b02017-01-05 01:49:081// 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 Cheng23eed1d2017-08-18 02:42:3111#include "base/memory/ptr_util.h"
estade36f01b02017-01-05 01:49:0812#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"
enneeb8c638a2017-01-31 23:29:5618#include "ui/gfx/skia_paint_util.h"
estade36f01b02017-01-05 01:49:0819#include "ui/gfx/skia_util.h"
20
21namespace gfx {
22namespace {
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".
27class ShadowNineboxSource : public CanvasImageSource {
28 public:
29 ShadowNineboxSource(const std::vector<ShadowValue>& shadows,
30 float corner_radius)
Peter Kasting0aa18542019-06-22 05:35:0131 : CanvasImageSource(CalculateSize(shadows, corner_radius)),
estade36f01b02017-01-05 01:49:0832 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 {
enneaae3c562017-02-28 04:26:4540 cc::PaintFlags flags;
estade9ef3c962017-03-09 05:16:5941 flags.setLooper(CreateShadowDrawLooper(shadows_));
estade36f01b02017-01-05 01:49:0842 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.
enneaae3c562017-02-28 04:26:4553 flags.setColor(SK_ColorTRANSPARENT);
54 canvas->sk_canvas()->drawRRect(r_rect, flags);
estade36f01b02017-01-05 01:49:0855 }
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.
77using ShadowDetailsMap = std::map<std::pair<int, int>, ShadowDetails>;
scottmg5e65e3a2017-03-08 08:48:4678base::LazyInstance<ShadowDetailsMap>::DestructorAtExit g_shadow_cache =
79 LAZY_INSTANCE_INITIALIZER;
estade36f01b02017-01-05 01:49:0880
81} // namespace
82
83ShadowDetails::ShadowDetails() {}
84ShadowDetails::ShadowDetails(const ShadowDetails& other) = default;
85ShadowDetails::~ShadowDetails() {}
86
87const 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 Huang2b91ba72019-08-20 17:59:2093 auto insertion = g_shadow_cache.Get().emplace(
94 std::make_pair(elevation, corner_radius), ShadowDetails());
estade36f01b02017-01-05 01:49:0895 DCHECK(insertion.second);
96 ShadowDetails* shadow = &insertion.first->second;
xiaohuic08a57482017-06-21 22:20:0597 shadow->values = ShadowValue::MakeMdShadowValues(elevation);
vmpstr6d9996c82017-02-23 00:43:2598 auto* source = new ShadowNineboxSource(shadow->values, corner_radius);
Daniel Cheng23eed1d2017-08-18 02:42:3199 shadow->ninebox_image = ImageSkia(base::WrapUnique(source), source->size());
estade36f01b02017-01-05 01:49:08100 return *shadow;
101}
102
103} // namespace gfx