普通文本  |  174行  |  4.92 KB

// Copyright (c) 2012 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 "ui/gfx/animation/tween.h"

#include <math.h>

#if defined(OS_WIN)
#include <float.h>
#endif

#include <algorithm>

#include "base/basictypes.h"
#include "base/logging.h"
#include "ui/gfx/safe_integer_conversions.h"

namespace gfx {

// static
double Tween::CalculateValue(Tween::Type type, double state) {
  DCHECK_GE(state, 0);
  DCHECK_LE(state, 1);

  switch (type) {
    case EASE_IN:
      return pow(state, 2);

    case EASE_IN_2:
      return pow(state, 4);

    case EASE_IN_OUT:
      if (state < 0.5)
        return pow(state * 2, 2) / 2.0;
      return 1.0 - (pow((state - 1.0) * 2, 2) / 2.0);

    case FAST_IN_OUT:
      return (pow(state - 0.5, 3) + 0.125) / 0.25;

    case LINEAR:
      return state;

    case EASE_OUT_SNAP:
      state = 0.95 * (1.0 - pow(1.0 - state, 2));
      return state;

    case EASE_OUT:
      return 1.0 - pow(1.0 - state, 2);

    case SMOOTH_IN_OUT:
      return sin(state);

    case ZERO:
      return 0;
  }

  NOTREACHED();
  return state;
}

namespace {
uint8 FloatToColorByte(float f) {
  return std::min(std::max(ToRoundedInt(f * 255.f), 0), 255);
}

uint8 BlendColorComponents(uint8 start,
                           uint8 target,
                           float start_alpha,
                           float target_alpha,
                           float blended_alpha,
                           double progress) {
  // Since progress can be outside [0, 1], blending can produce a value outside
  // [0, 255].
  float blended_premultiplied = Tween::FloatValueBetween(
      progress, start / 255.f * start_alpha, target / 255.f * target_alpha);
  return FloatToColorByte(blended_premultiplied / blended_alpha);
}

}  // namespace

// static
SkColor Tween::ColorValueBetween(double value, SkColor start, SkColor target) {
  float start_a = SkColorGetA(start) / 255.f;
  float target_a = SkColorGetA(target) / 255.f;
  float blended_a = FloatValueBetween(value, start_a, target_a);
  if (blended_a <= 0.f)
    return SkColorSetARGB(0, 0, 0, 0);
  blended_a = std::min(blended_a, 1.f);

  uint8 blended_r = BlendColorComponents(SkColorGetR(start),
                                         SkColorGetR(target),
                                         start_a,
                                         target_a,
                                         blended_a,
                                         value);
  uint8 blended_g = BlendColorComponents(SkColorGetG(start),
                                         SkColorGetG(target),
                                         start_a,
                                         target_a,
                                         blended_a,
                                         value);
  uint8 blended_b = BlendColorComponents(SkColorGetB(start),
                                         SkColorGetB(target),
                                         start_a,
                                         target_a,
                                         blended_a,
                                         value);

  return SkColorSetARGB(
      FloatToColorByte(blended_a), blended_r, blended_g, blended_b);
}

// static
double Tween::DoubleValueBetween(double value, double start, double target) {
  return start + (target - start) * value;
}

// static
float Tween::FloatValueBetween(double value, float start, float target) {
  return static_cast<float>(start + (target - start) * value);
}

// static
int Tween::IntValueBetween(double value, int start, int target) {
  if (start == target)
    return start;
  double delta = static_cast<double>(target - start);
  if (delta < 0)
    delta--;
  else
    delta++;
#if defined(OS_WIN)
  return start + static_cast<int>(value * _nextafter(delta, 0));
#else
  return start + static_cast<int>(value * nextafter(delta, 0));
#endif
}

//static
int Tween::LinearIntValueBetween(double value, int start, int target) {
  return std::floor(0.5 + DoubleValueBetween(value, start, target));
}

// static
gfx::Rect Tween::RectValueBetween(double value,
                                  const gfx::Rect& start_bounds,
                                  const gfx::Rect& target_bounds) {
  return gfx::Rect(
      LinearIntValueBetween(value, start_bounds.x(), target_bounds.x()),
      LinearIntValueBetween(value, start_bounds.y(), target_bounds.y()),
      LinearIntValueBetween(value, start_bounds.width(), target_bounds.width()),
      LinearIntValueBetween(
          value, start_bounds.height(), target_bounds.height()));
}

// static
gfx::Transform Tween::TransformValueBetween(
    double value,
    const gfx::Transform& start_transform,
    const gfx::Transform& end_transform) {
  if (value >= 1.0)
    return end_transform;
  if (value <= 0.0)
    return start_transform;

  gfx::Transform to_return = end_transform;
  to_return.Blend(start_transform, value);

  return to_return;
}

}  // namespace gfx