blob: 987844142645540c4f5281fee6ccc25186c832e7 [file] [log] [blame]
// Copyright 2020 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 "chromecast/ui/display_settings/color_temperature_animation.h"
#include <limits>
#include <vector>
#include "base/numerics/ranges.h"
#include "base/time/time.h"
#include "chromecast/graphics/cast_window_manager.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/compositor/compositor.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
#if defined(USE_AURA)
#include "chromecast/browser/cast_display_configurator.h"
#endif // defined(USE_AURA)
namespace chromecast {
namespace {
constexpr base::TimeDelta kManualAnimationDuration =
base::TimeDelta::FromSeconds(1);
const int kAnimationFrameRate = 30;
float Interpolate(const std::vector<float>& vec, float idx) {
size_t i = idx;
if (i == idx)
return vec[i];
float frac = idx - i;
return frac * vec[i + 1] + (1 - frac) * vec[i];
}
} // namespace
ColorTemperatureAnimation::ColorTemperatureAnimation(
CastWindowManager* window_manager,
shell::CastDisplayConfigurator* display_configurator,
const DisplaySettingsManager::ColorTemperatureConfig& config)
: gfx::LinearAnimation(kManualAnimationDuration,
kAnimationFrameRate,
nullptr),
window_manager_(window_manager),
display_configurator_(display_configurator),
config_(config),
start_temperature_(config.neutral_temperature),
current_temperature_(config.neutral_temperature),
target_temperature_(config_.neutral_temperature) {
DCHECK(window_manager_);
#if defined(USE_AURA)
DCHECK(display_configurator_);
#endif // defined(USE_AURA)
ApplyValuesToDisplay();
}
ColorTemperatureAnimation::~ColorTemperatureAnimation() = default;
void ColorTemperatureAnimation::AnimateToNewValue(float new_target_temperature,
base::TimeDelta duration) {
start_temperature_ = current_temperature_;
target_temperature_ =
base::ClampToRange(new_target_temperature, 1000.0f, 20000.0f);
if (ui::ScopedAnimationDurationScaleMode::duration_multiplier() ==
ui::ScopedAnimationDurationScaleMode::ZERO_DURATION) {
// Animations are disabled. Apply the target temperature directly to the
// compositor.
current_temperature_ = target_temperature_;
ApplyValuesToDisplay();
Stop();
return;
}
// This will reset the animation timer to the beginning.
SetDuration(duration);
Start();
}
void ColorTemperatureAnimation::AnimateToNeutral(base::TimeDelta duration) {
AnimateToNewValue(config_.neutral_temperature, duration);
}
void ColorTemperatureAnimation::AnimateToState(double state) {
state = base::ClampToRange(state, 0.0, 1.0);
current_temperature_ =
start_temperature_ + (target_temperature_ - start_temperature_) * state;
ApplyValuesToDisplay();
}
void ColorTemperatureAnimation::ApplyValuesToDisplay() {
// Clamp temperature value to table range.
float kelvin = base::ClampToRange(current_temperature_,
config_.temperature_values.front(),
config_.temperature_values.back());
size_t i = 0;
// Find greatest index whose value is <= |kelvin|. This is safe since |kelvin|
// is clamped to fall within the table range.
while (kelvin > config_.temperature_values[i + 1])
++i;
// Backwards interpolate the index from the temperature table.
float i_interp = i + (kelvin - config_.temperature_values[i]) /
(config_.temperature_values[i + 1] -
config_.temperature_values[i]);
float red_scale =
Interpolate(config_.red_values, i_interp) / config_.full_color;
float green_scale =
Interpolate(config_.green_values, i_interp) / config_.full_color;
float blue_scale =
Interpolate(config_.blue_values, i_interp) / config_.full_color;
DVLOG(2) << "RGB scaling: {" << red_scale << ", " << green_scale << ", "
<< blue_scale << "}";
if (display_configurator_) {
#if defined(USE_AURA)
DVLOG(1) << "Color temperature set to " << kelvin << " kelvin.";
// 3x3 color matrix, represented as a vector of length 9.
std::vector<float> color_matrix = {red_scale, 0, 0, 0, green_scale,
0, 0, 0, blue_scale};
display_configurator_->SetColorMatrix(color_matrix);
// The CTM is applied on the next swap buffers, so we need to make sure the
// root window triggers a swap buffer otherwise the content will not update.
window_manager_->GetRootWindow()
->GetHost()
->compositor()
->ScheduleFullRedraw();
#endif // defined(USE_AURA)
}
}
} // namespace chromecast