#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;
constexpr base::TimeDelta kShortFrameDurationThreshold = base::Milliseconds(10);
constexpr base::TimeDelta kAdjustedDurationForShortFrames =
base::Milliseconds(100);
}
QuickInsertGifView::QuickInsertGifView(
FramesFetcher frames_fetcher,
PreviewImageFetcher preview_image_fetcher,
const gfx::Size& original_dimensions)
: original_dimensions_(original_dimensions) {
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);
SetClipPath(SkPath::RRect(SkRRect::MakeRectXY(
gfx::RectToSkRect(GetImageBounds()), kQuickInsertGifCornerRadius,
kQuickInsertGifCornerRadius)));
}
void QuickInsertGifView::UpdateFrame() {
CHECK(next_frame_index_ < frames_.size());
if (!GetVisibleBounds().IsEmpty()) {
SetImage(ui::ImageModel::FromImageSkia(frames_[next_frame_index_].image));
}
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()) {
return;
}
frames_.reserve(frames.size());
for (auto& frame : frames) {
if (frame.duration <= kShortFrameDurationThreshold) {
frame.duration = kAdjustedDurationForShortFrames;
}
frames_.push_back(std::move(frame));
}
next_frame_index_ = 0;
UpdateFrame();
RecordFetchFramesTime();
}
void QuickInsertGifView::OnPreviewImageFetched(
const gfx::ImageSkia& preview_image) {
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),
50);
fetch_frames_start_time_ = std::nullopt;
}
}
BEGIN_METADATA(QuickInsertGifView)
END_METADATA
}