// 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 #include #include "base/lazy_instance.h" #include "base/memory/ptr_util.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_paint_util.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& shadows, float corner_radius) : CanvasImageSource(CalculateSize(shadows, corner_radius)), shadows_(shadows), corner_radius_(corner_radius) { DCHECK(!shadows.empty()); } ~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& 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 shadows_; const float corner_radius_; DISALLOW_COPY_AND_ASSIGN(ShadowNineboxSource); }; // Map from elevation/corner radius pair to a cached shadow. using ShadowDetailsMap = std::map, ShadowDetails>; base::LazyInstance::DestructorAtExit 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().emplace( std::make_pair(elevation, corner_radius), ShadowDetails()); DCHECK(insertion.second); ShadowDetails* shadow = &insertion.first->second; shadow->values = ShadowValue::MakeMdShadowValues(elevation); auto* source = new ShadowNineboxSource(shadow->values, corner_radius); shadow->ninebox_image = ImageSkia(base::WrapUnique(source), source->size()); return *shadow; } } // namespace gfx