blob: 00baa84b260b1e604158002f4dc4087697a14185 [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 "ash/quick_insert/views/quick_insert_gif_view.h"
#include <optional>
#include "ash/public/cpp/image_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/metrics/histogram_functions.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/models/image_model.h"
#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/geometry/skia_conversions.h"
#include "ui/gfx/image/image_skia_operations.h"
#include "ui/views/background.h"
#include "ui/views/controls/image_view.h"
namespace ash {
namespace {
constexpr int kQuickInsertGifCornerRadius = 8;
// We use a duration of 100ms for frames that specify a duration of <= 10ms.
// This is to follow the behavior of blink (see http://webkit.org/b/36082 for
// more information).
constexpr base::TimeDelta kShortFrameDurationThreshold = base::Milliseconds(10);
constexpr base::TimeDelta kAdjustedDurationForShortFrames =
base::Milliseconds(100);
} // namespace
QuickInsertGifView::QuickInsertGifView(
FramesFetcher frames_fetcher,
PreviewImageFetcher preview_image_fetcher,
const gfx::Size& original_dimensions)
: original_dimensions_(original_dimensions) {
// Show a placeholder rect while the gif loads.
views::Builder<QuickInsertGifView>(this)
.SetBackground(views::CreateRoundedRectBackground(
cros_tokens::kCrosSysAppBaseShaded, kQuickInsertGifCornerRadius))
.SetImage(ui::ImageModel::FromImageSkia(
image_util::CreateEmptyImage(original_dimensions)))
.BuildChildren();
fetch_frames_start_time_ = base::TimeTicks::Now();
preview_request_ =
std::move(preview_image_fetcher)
.Run(base::BindOnce(&QuickInsertGifView::OnPreviewImageFetched,
weak_factory_.GetWeakPtr()));
frames_request_ =
std::move(frames_fetcher)
.Run(base::BindOnce(&QuickInsertGifView::OnFramesFetched,
weak_factory_.GetWeakPtr()));
}
QuickInsertGifView::~QuickInsertGifView() = default;
void QuickInsertGifView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
views::ImageView::OnBoundsChanged(previous_bounds);
SkPath path;
path.addRoundRect(gfx::RectToSkRect(GetImageBounds()),
SkIntToScalar(kQuickInsertGifCornerRadius),
SkIntToScalar(kQuickInsertGifCornerRadius));
SetClipPath(path);
}
void QuickInsertGifView::UpdateFrame() {
CHECK(next_frame_index_ < frames_.size());
// Don't update the frame image if the view is not visible, but keep the timer
// going.
if (!GetVisibleBounds().IsEmpty()) {
SetImage(ui::ImageModel::FromImageSkia(frames_[next_frame_index_].image));
}
// Schedule next frame update.
update_frame_timer_.Start(FROM_HERE, frames_[next_frame_index_].duration,
base::BindOnce(&QuickInsertGifView::UpdateFrame,
weak_factory_.GetWeakPtr()));
next_frame_index_ = (next_frame_index_ + 1) % frames_.size();
}
void QuickInsertGifView::OnFramesFetched(
std::vector<image_util::AnimationFrame> frames) {
if (frames.empty()) {
// TODO: b/316936723 - Handle frames being empty.
return;
}
frames_.reserve(frames.size());
for (auto& frame : frames) {
if (frame.duration <= kShortFrameDurationThreshold) {
frame.duration = kAdjustedDurationForShortFrames;
}
frames_.push_back(std::move(frame));
}
// Start gif from the first frame.
next_frame_index_ = 0;
UpdateFrame();
RecordFetchFramesTime();
}
void QuickInsertGifView::OnPreviewImageFetched(
const gfx::ImageSkia& preview_image) {
// Only show preview image if gif frames have not already been fetched.
if (frames_.empty()) {
SetImage(ui::ImageModel::FromImageSkia(preview_image));
if (!preview_image.isNull()) {
RecordFetchFramesTime();
}
}
}
void QuickInsertGifView::RecordFetchFramesTime() {
if (fetch_frames_start_time_.has_value()) {
UmaHistogramCustomTimes("Ash.Picker.TimeToFirstGifFrame",
base::TimeTicks::Now() - *fetch_frames_start_time_,
base::Milliseconds(0), base::Seconds(1),
/*buckets=*/50);
fetch_frames_start_time_ = std::nullopt;
}
}
BEGIN_METADATA(QuickInsertGifView)
END_METADATA
} // namespace ash