| // 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 |