File: shadow_util.cc

package info (click to toggle)
chromium 138.0.7204.183-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 6,071,908 kB
  • sloc: cpp: 34,937,088; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,806; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (165 lines) | stat: -rw-r--r-- 6,098 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/gfx/shadow_util.h"

#include <map>
#include <vector>

#include "base/lazy_instance.h"
#include "base/memory/ptr_util.h"
#include "third_party/skia/include/core/SkRRect.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/skia_conversions.h"
#include "ui/gfx/image/canvas_image_source.h"
#include "ui/gfx/shadow_value.h"
#include "ui/gfx/skia_paint_util.h"

namespace gfx {
namespace {

// Creates an image with the given shadows painted around a round rect with
// the given corner radius. The image will be just large enough to paint the
// shadows appropriately with a 1px square region reserved for "content".
class ShadowNineboxSource : public CanvasImageSource {
 public:
  ShadowNineboxSource(const std::vector<ShadowValue>& shadows,
                      float corner_radius)
      : CanvasImageSource(CalculateSize(shadows, corner_radius)),
        shadows_(shadows),
        corner_radius_(corner_radius) {
    DCHECK(!shadows.empty());
  }

  ShadowNineboxSource(const ShadowNineboxSource&) = delete;
  ShadowNineboxSource& operator=(const ShadowNineboxSource&) = delete;

  ~ShadowNineboxSource() override {}

  // CanvasImageSource overrides:
  void Draw(Canvas* canvas) override {
    cc::PaintFlags flags;
    flags.setLooper(CreateShadowDrawLooper(shadows_));
    Insets insets = -ShadowValue::GetMargin(shadows_);
    gfx::Rect bounds(size());
    bounds.Inset(insets);
    SkRRect r_rect = SkRRect::MakeRectXY(gfx::RectToSkRect(bounds),
                                         corner_radius_, corner_radius_);

    // Clip out the center so it's not painted with the shadow.
    canvas->sk_canvas()->clipRRect(r_rect, SkClipOp::kDifference, true);
    // Clipping alone is not enough --- due to anti aliasing there will still be
    // some of the fill color in the rounded corners. We must make the fill
    // color transparent.
    flags.setColor(SK_ColorTRANSPARENT);
    canvas->sk_canvas()->drawRRect(r_rect, flags);
  }

 private:
  static Size CalculateSize(const std::vector<ShadowValue>& shadows,
                            float corner_radius) {
    // The "content" area (the middle tile in the 3x3 grid) is a single pixel.
    gfx::Rect bounds(0, 0, 1, 1);
    // We need enough space to render the full range of blur.
    bounds.Inset(-ShadowValue::GetBlurRegion(shadows));
    // We also need space for the full roundrect corner rounding.
    bounds.Inset(-gfx::Insets(corner_radius));
    return bounds.size();
  }

  const std::vector<ShadowValue> shadows_;

  const float corner_radius_;
};

// A shadow's appearance is determined by its rounded corner radius and shadow
// values. Make these attributes as the key for shadow details.
struct ShadowDetailsKey {
  bool operator==(const ShadowDetailsKey& other) const {
    return (corner_radius == other.corner_radius) && (values == other.values);
  }

  bool operator<(const ShadowDetailsKey& other) const {
    return (corner_radius < other.corner_radius) ||
           ((corner_radius == other.corner_radius) && (values < other.values));
  }
  int corner_radius;
  ShadowValues values;
};

// Map from shadow details key to a cached shadow.
using ShadowDetailsMap = std::map<ShadowDetailsKey, ShadowDetails>;
base::LazyInstance<ShadowDetailsMap>::DestructorAtExit g_shadow_cache =
    LAZY_INSTANCE_INITIALIZER;

}  // namespace

ShadowDetails::ShadowDetails(const gfx::ShadowValues& values,
                             const gfx::ImageSkia& nine_patch_image)
    : values(values), nine_patch_image(nine_patch_image) {}
ShadowDetails::ShadowDetails(const ShadowDetails& other) = default;
ShadowDetails::~ShadowDetails() {}

const ShadowDetails& ShadowDetails::Get(int elevation,
                                        int corner_radius,
                                        ShadowStyle style) {
  switch (style) {
    case ShadowStyle::kMaterialDesign:
      return Get(corner_radius, ShadowValue::MakeMdShadowValues(elevation));
#if BUILDFLAG(IS_CHROMEOS)
    case ShadowStyle::kChromeOSSystemUI:
      return Get(corner_radius,
                 ShadowValue::MakeChromeOSSystemUIShadowValues(elevation));
#endif
  }
}

const ShadowDetails& ShadowDetails::Get(int elevation,
                                        int radius,
                                        SkColor key_color,
                                        SkColor ambient_color,
                                        ShadowStyle style) {
  switch (style) {
    case ShadowStyle::kMaterialDesign:
      return Get(radius, ShadowValue::MakeMdShadowValues(elevation, key_color,
                                                         ambient_color));
#if BUILDFLAG(IS_CHROMEOS)
    case ShadowStyle::kChromeOSSystemUI:
      return Get(radius, ShadowValue::MakeChromeOSSystemUIShadowValues(
                             elevation, key_color, ambient_color));
#endif
  }
}

const ShadowDetails& ShadowDetails::Get(int radius,
                                        const gfx::ShadowValues& values) {
  ShadowDetailsKey key{radius, values};
  auto iter = g_shadow_cache.Get().find(key);
  if (iter != g_shadow_cache.Get().end()) {
    return iter->second;
  }

  // Evict the details whose ninebox image does not have any shadow owners.
  std::erase_if(g_shadow_cache.Get(), [](auto& pair) {
    return pair.second.nine_patch_image.IsUniquelyOwned();
  });

  auto source =
      std::make_unique<ShadowNineboxSource>(values, key.corner_radius);
  const gfx::Size image_size = source->size();
  auto nine_patch_image = ImageSkia(std::move(source), image_size);
  auto insertion = g_shadow_cache.Get().emplace(
      key, ShadowDetails(values, nine_patch_image));
  DCHECK(insertion.second);
  const std::pair<const ShadowDetailsKey, ShadowDetails>& inserted_item =
      *(insertion.first);
  return inserted_item.second;
}

size_t ShadowDetails::GetDetailsCacheSizeForTest() {
  return g_shadow_cache.Get().size();
}

}  // namespace gfx