blob: be0064983f854036d49ca88841b944c50d1fa1c6 [file] [log] [blame]
// Copyright 2015 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 "third_party/blink/renderer/core/animation/css_color_interpolation_type.h"
#include <memory>
#include <utility>
#include "base/memory/ptr_util.h"
#include "third_party/blink/renderer/core/animation/color_property_functions.h"
#include "third_party/blink/renderer/core/css/css_color_value.h"
#include "third_party/blink/renderer/core/css/css_identifier_value.h"
#include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
#include "third_party/blink/renderer/core/layout/layout_theme.h"
namespace blink {
using namespace cssvalue;
enum InterpolableColorIndex : unsigned {
kRed,
kGreen,
kBlue,
kAlpha,
kCurrentcolor,
kWebkitActivelink,
kWebkitLink,
kQuirkInherit,
kInterpolableColorIndexCount,
};
static std::unique_ptr<InterpolableValue> CreateInterpolableColorForIndex(
InterpolableColorIndex index) {
DCHECK_LT(index, kInterpolableColorIndexCount);
std::unique_ptr<InterpolableList> list =
InterpolableList::Create(kInterpolableColorIndexCount);
for (unsigned i = 0; i < kInterpolableColorIndexCount; i++)
list->Set(i, InterpolableNumber::Create(i == index));
return std::move(list);
}
std::unique_ptr<InterpolableValue>
CSSColorInterpolationType::CreateInterpolableColor(const Color& color) {
std::unique_ptr<InterpolableList> list =
InterpolableList::Create(kInterpolableColorIndexCount);
list->Set(kRed, InterpolableNumber::Create(color.Red() * color.Alpha()));
list->Set(kGreen, InterpolableNumber::Create(color.Green() * color.Alpha()));
list->Set(kBlue, InterpolableNumber::Create(color.Blue() * color.Alpha()));
list->Set(kAlpha, InterpolableNumber::Create(color.Alpha()));
list->Set(kCurrentcolor, InterpolableNumber::Create(0));
list->Set(kWebkitActivelink, InterpolableNumber::Create(0));
list->Set(kWebkitLink, InterpolableNumber::Create(0));
list->Set(kQuirkInherit, InterpolableNumber::Create(0));
return std::move(list);
}
std::unique_ptr<InterpolableValue>
CSSColorInterpolationType::CreateInterpolableColor(CSSValueID keyword) {
switch (keyword) {
case CSSValueCurrentcolor:
return CreateInterpolableColorForIndex(kCurrentcolor);
case CSSValueWebkitActivelink:
return CreateInterpolableColorForIndex(kWebkitActivelink);
case CSSValueWebkitLink:
return CreateInterpolableColorForIndex(kWebkitLink);
case CSSValueInternalQuirkInherit:
return CreateInterpolableColorForIndex(kQuirkInherit);
case CSSValueWebkitFocusRingColor:
return CreateInterpolableColor(LayoutTheme::GetTheme().FocusRingColor());
default:
DCHECK(StyleColor::IsColorKeyword(keyword));
return CreateInterpolableColor(StyleColor::ColorFromKeyword(keyword));
}
}
std::unique_ptr<InterpolableValue>
CSSColorInterpolationType::CreateInterpolableColor(const StyleColor& color) {
if (color.IsCurrentColor())
return CreateInterpolableColorForIndex(kCurrentcolor);
return CreateInterpolableColor(color.GetColor());
}
std::unique_ptr<InterpolableValue>
CSSColorInterpolationType::MaybeCreateInterpolableColor(const CSSValue& value) {
if (auto* color_value = DynamicTo<CSSColorValue>(value))
return CreateInterpolableColor(color_value->Value());
auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
if (!identifier_value)
return nullptr;
if (!StyleColor::IsColorKeyword(identifier_value->GetValueID()))
return nullptr;
return CreateInterpolableColor(identifier_value->GetValueID());
}
static void AddPremultipliedColor(double& red,
double& green,
double& blue,
double& alpha,
double fraction,
const Color& color) {
double color_alpha = color.Alpha();
red += fraction * color.Red() * color_alpha;
green += fraction * color.Green() * color_alpha;
blue += fraction * color.Blue() * color_alpha;
alpha += fraction * color_alpha;
}
Color CSSColorInterpolationType::ResolveInterpolableColor(
const InterpolableValue& interpolable_color,
const StyleResolverState& state,
bool is_visited,
bool is_text_decoration) {
const InterpolableList& list = ToInterpolableList(interpolable_color);
DCHECK_EQ(list.length(), kInterpolableColorIndexCount);
double red = ToInterpolableNumber(list.Get(kRed))->Value();
double green = ToInterpolableNumber(list.Get(kGreen))->Value();
double blue = ToInterpolableNumber(list.Get(kBlue))->Value();
double alpha = ToInterpolableNumber(list.Get(kAlpha))->Value();
if (double currentcolor_fraction =
ToInterpolableNumber(list.Get(kCurrentcolor))->Value()) {
auto current_color_getter = is_visited
? ColorPropertyFunctions::GetVisitedColor
: ColorPropertyFunctions::GetUnvisitedColor;
StyleColor current_style_color = StyleColor::CurrentColor();
if (is_text_decoration) {
current_style_color =
current_color_getter(CSSProperty::Get(CSSPropertyWebkitTextFillColor),
*state.Style())
.Access();
}
if (current_style_color.IsCurrentColor()) {
current_style_color =
current_color_getter(CSSProperty::Get(CSSPropertyColor),
*state.Style())
.Access();
}
AddPremultipliedColor(red, green, blue, alpha, currentcolor_fraction,
current_style_color.GetColor());
}
const TextLinkColors& colors = state.GetDocument().GetTextLinkColors();
if (double webkit_activelink_fraction =
ToInterpolableNumber(list.Get(kWebkitActivelink))->Value())
AddPremultipliedColor(red, green, blue, alpha, webkit_activelink_fraction,
colors.ActiveLinkColor());
if (double webkit_link_fraction =
ToInterpolableNumber(list.Get(kWebkitLink))->Value())
AddPremultipliedColor(
red, green, blue, alpha, webkit_link_fraction,
is_visited ? colors.VisitedLinkColor() : colors.LinkColor());
if (double quirk_inherit_fraction =
ToInterpolableNumber(list.Get(kQuirkInherit))->Value())
AddPremultipliedColor(red, green, blue, alpha, quirk_inherit_fraction,
colors.TextColor());
alpha = clampTo<double>(alpha, 0, 255);
if (alpha == 0)
return Color::kTransparent;
return MakeRGBA(
clampTo<int>(round(red / alpha)), clampTo<int>(round(green / alpha)),
clampTo<int>(round(blue / alpha)), clampTo<int>(round(alpha)));
}
class InheritedColorChecker
: public CSSInterpolationType::CSSConversionChecker {
public:
InheritedColorChecker(const CSSProperty& property,
const OptionalStyleColor& color)
: property_(property), color_(color) {}
private:
bool IsValid(const StyleResolverState& state,
const InterpolationValue& underlying) const final {
return color_ == ColorPropertyFunctions::GetUnvisitedColor(
property_, *state.ParentStyle());
}
const CSSProperty& property_;
const OptionalStyleColor color_;
};
InterpolationValue CSSColorInterpolationType::MaybeConvertNeutral(
const InterpolationValue&,
ConversionCheckers&) const {
return ConvertStyleColorPair(StyleColor(Color::kTransparent),
StyleColor(Color::kTransparent));
}
InterpolationValue CSSColorInterpolationType::MaybeConvertInitial(
const StyleResolverState&,
ConversionCheckers& conversion_checkers) const {
OptionalStyleColor initial_color =
ColorPropertyFunctions::GetInitialColor(CssProperty());
if (initial_color.IsNull())
return nullptr;
return ConvertStyleColorPair(initial_color.Access(), initial_color.Access());
}
InterpolationValue CSSColorInterpolationType::MaybeConvertInherit(
const StyleResolverState& state,
ConversionCheckers& conversion_checkers) const {
if (!state.ParentStyle())
return nullptr;
// Visited color can never explicitly inherit from parent visited color so
// only use the unvisited color.
OptionalStyleColor inherited_color =
ColorPropertyFunctions::GetUnvisitedColor(CssProperty(),
*state.ParentStyle());
conversion_checkers.push_back(
std::make_unique<InheritedColorChecker>(CssProperty(), inherited_color));
return ConvertStyleColorPair(inherited_color, inherited_color);
}
enum InterpolableColorPairIndex : unsigned {
kUnvisited,
kVisited,
kInterpolableColorPairIndexCount,
};
InterpolationValue CSSColorInterpolationType::MaybeConvertValue(
const CSSValue& value,
const StyleResolverState* state,
ConversionCheckers& conversion_checkers) const {
if (CssProperty().PropertyID() == CSSPropertyColor) {
auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
if (identifier_value &&
identifier_value->GetValueID() == CSSValueCurrentcolor) {
DCHECK(state);
return MaybeConvertInherit(*state, conversion_checkers);
}
}
std::unique_ptr<InterpolableValue> interpolable_color =
MaybeCreateInterpolableColor(value);
if (!interpolable_color)
return nullptr;
std::unique_ptr<InterpolableList> color_pair =
InterpolableList::Create(kInterpolableColorPairIndexCount);
color_pair->Set(kUnvisited, interpolable_color->Clone());
color_pair->Set(kVisited, std::move(interpolable_color));
return InterpolationValue(std::move(color_pair));
}
InterpolationValue CSSColorInterpolationType::ConvertStyleColorPair(
const OptionalStyleColor& unvisited_color,
const OptionalStyleColor& visited_color) const {
if (unvisited_color.IsNull() || visited_color.IsNull()) {
return nullptr;
}
std::unique_ptr<InterpolableList> color_pair =
InterpolableList::Create(kInterpolableColorPairIndexCount);
color_pair->Set(kUnvisited,
CreateInterpolableColor(unvisited_color.Access()));
color_pair->Set(kVisited, CreateInterpolableColor(visited_color.Access()));
return InterpolationValue(std::move(color_pair));
}
InterpolationValue
CSSColorInterpolationType::MaybeConvertStandardPropertyUnderlyingValue(
const ComputedStyle& style) const {
return ConvertStyleColorPair(
ColorPropertyFunctions::GetUnvisitedColor(CssProperty(), style),
ColorPropertyFunctions::GetVisitedColor(CssProperty(), style));
}
void CSSColorInterpolationType::ApplyStandardPropertyValue(
const InterpolableValue& interpolable_value,
const NonInterpolableValue*,
StyleResolverState& state) const {
const InterpolableList& color_pair = ToInterpolableList(interpolable_value);
DCHECK_EQ(color_pair.length(), kInterpolableColorPairIndexCount);
ColorPropertyFunctions::SetUnvisitedColor(
CssProperty(), *state.Style(),
ResolveInterpolableColor(
*color_pair.Get(kUnvisited), state, false,
CssProperty().PropertyID() == CSSPropertyTextDecorationColor));
ColorPropertyFunctions::SetVisitedColor(
CssProperty(), *state.Style(),
ResolveInterpolableColor(
*color_pair.Get(kVisited), state, true,
CssProperty().PropertyID() == CSSPropertyTextDecorationColor));
}
const CSSValue* CSSColorInterpolationType::CreateCSSValue(
const InterpolableValue& interpolable_value,
const NonInterpolableValue*,
const StyleResolverState& state) const {
const InterpolableList& color_pair = ToInterpolableList(interpolable_value);
Color color = ResolveInterpolableColor(*color_pair.Get(kUnvisited), state);
return CSSColorValue::Create(color.Rgb());
}
} // namespace blink