blob: 85e2418a8ec81c482ebf942e7f7c7670bb0ab0e1 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/lens/lens_overlay_blur_layer_delegate.h"
#include "base/compiler_specific.h"
#include "base/timer/timer.h"
#include "cc/paint/render_surface_filters.h"
#include "components/lens/lens_features.h"
#include "ui/compositor/paint_recorder.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/geometry/rect_f.h"
namespace {
bool AreBitmapsEqual(const SkBitmap& bitmap1, const SkBitmap& bitmap2) {
// Verify the dimensions are the same.
if (bitmap1.width() != bitmap2.width() ||
bitmap1.height() != bitmap2.height()) {
return false;
}
// Compare pixel data
SkPixmap pixmap1 = bitmap1.pixmap();
SkPixmap pixmap2 = bitmap2.pixmap();
return UNSAFE_TODO(memcmp(pixmap1.addr(), pixmap2.addr(),
pixmap1.computeByteSize())) == 0;
}
} // namespace
namespace lens {
LensOverlayBlurLayerDelegate::LensOverlayBlurLayerDelegate(
content::RenderWidgetHost* background_view_host)
: background_view_host_(background_view_host) {
SetLayer(std::make_unique<ui::Layer>(ui::LAYER_TEXTURED));
layer()->SetFillsBoundsOpaquely(true);
layer()->set_delegate(this);
render_widget_host_observer_.Observe(background_view_host);
}
LensOverlayBlurLayerDelegate::~LensOverlayBlurLayerDelegate() = default;
void LensOverlayBlurLayerDelegate::StartBackgroundImageCapture() {
// If there is no background_view_host_, there is nothing to take a screenshot
// of, so we should exit early.
if (IsLiveBlurActive() || !background_view_host_) {
return;
}
// Start taking screenshots to render on the layer.
screenshot_timer_.Start(
FROM_HERE,
base::Hertz(lens::features::GetLensOverlayCustomBlurRefreshRateHertz()),
base::BindRepeating(&LensOverlayBlurLayerDelegate::FetchBackgroundImage,
weak_factory_.GetWeakPtr()));
}
void LensOverlayBlurLayerDelegate::StopBackgroundImageCapture() {
if (!IsLiveBlurActive()) {
return;
}
screenshot_timer_.Stop();
}
bool LensOverlayBlurLayerDelegate::IsLiveBlurActive() {
return screenshot_timer_.IsRunning();
}
bool LensOverlayBlurLayerDelegate::IsCapturingBackgroundImageForTesting() {
return screenshot_timer_.IsRunning();
}
void LensOverlayBlurLayerDelegate::OnPaintLayer(
const ui::PaintContext& context) {
if (background_screenshot_.drawsNothing()) {
return;
}
// Create the blur filter to apply to the downsampled image.
cc::FilterOperations operations;
operations.Append(cc::FilterOperation::CreateBlurFilter(
lens::features::GetLensOverlayCustomBlurBlurRadiusPixels(),
SkTileMode::kClamp));
sk_sp<cc::PaintFilter> filter =
cc::RenderSurfaceFilters::BuildImageFilter(operations);
// Create a paint object with the filter
cc::PaintFlags filter_flags;
filter_flags.setImageFilter(filter);
// Configure `canvas`.
gfx::SizeF layer_size(layer()->size());
ui::PaintRecorder recorder(context, gfx::ToFlooredSize(layer_size));
gfx::Canvas* const canvas = recorder.canvas();
gfx::ImageSkia image =
gfx::ImageSkia::CreateFromBitmap(background_screenshot_, /*scale=*/1.f);
canvas->DrawImageInt(
image, /*src_x=*/0, /*src_y=*/0, /*src_w=*/background_screenshot_.width(),
/*src_h=*/background_screenshot_.height(), /*dest_x=*/0, /*dest_y=*/0,
/*_dest_w_=*/layer_size.width(), /*dest_h=*/layer_size.height(),
/*filter=*/false, filter_flags);
}
void LensOverlayBlurLayerDelegate::OnDeviceScaleFactorChanged(
float old_device_scale_factor,
float new_device_scale_factor) {
// Do nothing. OnPaintLayer will automatically repaint with a new image.
}
void LensOverlayBlurLayerDelegate::RenderWidgetHostDestroyed(
content::RenderWidgetHost* widget_host) {
CHECK(widget_host == background_view_host_);
render_widget_host_observer_.Reset();
background_view_host_ = nullptr;
// If the host view was destroyed, stop updating the background blur.
StopBackgroundImageCapture();
}
void LensOverlayBlurLayerDelegate::FetchBackgroundImage() {
if (!background_view_host_ || !background_view_host_->GetView()) {
return;
}
auto* view = background_view_host_->GetView();
auto size = view->GetViewBounds().size();
auto quality = lens::features::GetLensOverlayCustomBlurQuality();
view->CopyFromSurface(
/*src_rect=*/gfx::Rect(),
/*output_size=*/
gfx::Size(size.width() * quality, size.height() * quality),
base::BindOnce(&LensOverlayBlurLayerDelegate::UpdateBackgroundImage,
weak_factory_.GetWeakPtr()));
}
void LensOverlayBlurLayerDelegate::UpdateBackgroundImage(
const SkBitmap& bitmap) {
auto layer_size = layer()->size();
if (bitmap.drawsNothing() || layer_size.width() * layer_size.height() <= 0 ||
AreBitmapsEqual(background_screenshot_, bitmap)) {
return;
}
background_screenshot_ = bitmap;
layer()->SchedulePaint(gfx::Rect(layer_size));
}
} // namespace lens