| Index: ui/gfx/shadow_util.cc
|
| diff --git a/ui/gfx/shadow_util.cc b/ui/gfx/shadow_util.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..238181af1d46604001b05c5259d73bb3002426c9
|
| --- /dev/null
|
| +++ b/ui/gfx/shadow_util.cc
|
| @@ -0,0 +1,113 @@
|
| +// Copyright 2017 The Chromium Authors. All rights reserved.
|
| +// 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 "third_party/skia/include/core/SkDrawLooper.h"
|
| +#include "third_party/skia/include/core/SkRRect.h"
|
| +#include "ui/gfx/canvas.h"
|
| +#include "ui/gfx/geometry/insets.h"
|
| +#include "ui/gfx/image/canvas_image_source.h"
|
| +#include "ui/gfx/shadow_value.h"
|
| +#include "ui/gfx/skia_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), false),
|
| + shadows_(shadows),
|
| + corner_radius_(corner_radius) {
|
| + DCHECK(!shadows.empty());
|
| + }
|
| + ~ShadowNineboxSource() override {}
|
| +
|
| + // CanvasImageSource overrides:
|
| + void Draw(Canvas* canvas) override {
|
| + SkPaint paint;
|
| + paint.setLooper(CreateShadowDrawLooperCorrectBlur(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.
|
| + paint.setColor(SK_ColorTRANSPARENT);
|
| + canvas->sk_canvas()->drawRRect(r_rect, paint);
|
| + }
|
| +
|
| + 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_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(ShadowNineboxSource);
|
| +};
|
| +
|
| +// Map from elevation/corner radius pair to a cached shadow.
|
| +using ShadowDetailsMap = std::map<std::pair<int, int>, ShadowDetails>;
|
| +base::LazyInstance<ShadowDetailsMap> g_shadow_cache = LAZY_INSTANCE_INITIALIZER;
|
| +
|
| +} // namespace
|
| +
|
| +ShadowDetails::ShadowDetails() {}
|
| +ShadowDetails::ShadowDetails(const ShadowDetails& other) = default;
|
| +ShadowDetails::~ShadowDetails() {}
|
| +
|
| +const ShadowDetails& ShadowDetails::Get(int elevation, int corner_radius) {
|
| + auto iter =
|
| + g_shadow_cache.Get().find(std::make_pair(elevation, corner_radius));
|
| + if (iter != g_shadow_cache.Get().end())
|
| + return iter->second;
|
| +
|
| + auto insertion = g_shadow_cache.Get().insert(std::make_pair(
|
| + std::make_pair(elevation, corner_radius), ShadowDetails()));
|
| + DCHECK(insertion.second);
|
| + ShadowDetails* shadow = &insertion.first->second;
|
| + // To match the CSS notion of blur (spread outside the bounding box) to the
|
| + // Skia notion of blur (spread outside and inside the bounding box), we have
|
| + // to double the designer-provided blur values.
|
| + const int kBlurCorrection = 2;
|
| + // "Key shadow": y offset is elevation and blur is twice the elevation.
|
| + shadow->values.emplace_back(gfx::Vector2d(0, elevation),
|
| + kBlurCorrection * elevation * 2,
|
| + SkColorSetA(SK_ColorBLACK, 0x3d));
|
| + // "Ambient shadow": no offset and blur matches the elevation.
|
| + shadow->values.emplace_back(gfx::Vector2d(), kBlurCorrection * elevation,
|
| + SkColorSetA(SK_ColorBLACK, 0x1f));
|
| + // To see what this looks like for elevation 24, try this CSS:
|
| + // box-shadow: 0 24px 48px rgba(0, 0, 0, .24),
|
| + // 0 0 24px rgba(0, 0, 0, .12);
|
| + auto source = new ShadowNineboxSource(shadow->values, corner_radius);
|
| + shadow->ninebox_image = ImageSkia(source, source->size());
|
| + return *shadow;
|
| +}
|
| +
|
| +} // namespace gfx
|
|
|