blob: 6a778388ae5162ebe7a2ae6298414ff698ba7662 [file] [log] [blame]
// Copyright 2013 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 <algorithm>
#include "base/values.h"
#include "cc/base/math_util.h"
#include "cc/output/filter_operation.h"
#include "third_party/skia/include/core/SkMath.h"
namespace cc {
bool FilterOperation::operator==(const FilterOperation& other) const {
if (type_ != other.type_)
return false;
if (type_ == COLOR_MATRIX)
return !memcmp(matrix_, other.matrix_, sizeof(matrix_));
if (type_ == DROP_SHADOW) {
return amount_ == other.amount_ &&
drop_shadow_offset_ == other.drop_shadow_offset_ &&
drop_shadow_color_ == other.drop_shadow_color_;
}
return amount_ == other.amount_;
}
FilterOperation::FilterOperation(FilterType type, float amount)
: type_(type),
amount_(amount),
drop_shadow_offset_(0, 0),
drop_shadow_color_(0),
zoom_inset_(0) {
DCHECK_NE(type_, DROP_SHADOW);
DCHECK_NE(type_, COLOR_MATRIX);
memset(matrix_, 0, sizeof(matrix_));
}
FilterOperation::FilterOperation(FilterType type,
gfx::Point offset,
float stdDeviation,
SkColor color)
: type_(type),
amount_(stdDeviation),
drop_shadow_offset_(offset),
drop_shadow_color_(color),
zoom_inset_(0) {
DCHECK_EQ(type_, DROP_SHADOW);
memset(matrix_, 0, sizeof(matrix_));
}
FilterOperation::FilterOperation(FilterType type, SkScalar matrix[20])
: type_(type),
amount_(0),
drop_shadow_offset_(0, 0),
drop_shadow_color_(0),
zoom_inset_(0) {
DCHECK_EQ(type_, COLOR_MATRIX);
memcpy(matrix_, matrix, sizeof(matrix_));
}
FilterOperation::FilterOperation(FilterType type, float amount, int inset)
: type_(type),
amount_(amount),
drop_shadow_offset_(0, 0),
drop_shadow_color_(0),
zoom_inset_(inset) {
DCHECK_EQ(type_, ZOOM);
memset(matrix_, 0, sizeof(matrix_));
}
// TODO(ajuma): Define a version of ui::Tween::ValueBetween for floats, and use
// that instead.
static float BlendFloats(float from, float to, double progress) {
return from * (1.0 - progress) + to * progress;
}
static int BlendInts(int from, int to, double progress) {
return static_cast<int>(
MathUtil::Round(from * (1.0 - progress) + to * progress));
}
static uint8_t BlendColorComponents(uint8_t from,
uint8_t to,
uint8_t from_alpha,
uint8_t to_alpha,
uint8_t blended_alpha,
double progress) {
// Since progress can be outside [0, 1], blending can produce a value outside
// [0, 255].
int blended_premultiplied = BlendInts(SkMulDiv255Round(from, from_alpha),
SkMulDiv255Round(to, to_alpha),
progress);
int blended = static_cast<int>(
MathUtil::Round(blended_premultiplied * 255.f / blended_alpha));
return static_cast<uint8_t>(MathUtil::ClampToRange(blended, 0, 255));
}
static SkColor BlendSkColors(SkColor from, SkColor to, double progress) {
int from_a = SkColorGetA(from);
int to_a = SkColorGetA(to);
int blended_a = BlendInts(from_a, to_a, progress);
if (blended_a <= 0)
return SkColorSetARGB(0, 0, 0, 0);
blended_a = std::min(blended_a, 255);
// TODO(ajuma): Use SkFourByteInterp once http://crbug.com/260369 is fixed.
uint8_t blended_r = BlendColorComponents(
SkColorGetR(from), SkColorGetR(to), from_a, to_a, blended_a, progress);
uint8_t blended_g = BlendColorComponents(
SkColorGetG(from), SkColorGetG(to), from_a, to_a, blended_a, progress);
uint8_t blended_b = BlendColorComponents(
SkColorGetB(from), SkColorGetB(to), from_a, to_a, blended_a, progress);
return SkColorSetARGB(blended_a, blended_r, blended_g, blended_b);
}
static FilterOperation CreateNoOpFilter(FilterOperation::FilterType type) {
switch (type) {
case FilterOperation::GRAYSCALE:
return FilterOperation::CreateGrayscaleFilter(0.f);
case FilterOperation::SEPIA:
return FilterOperation::CreateSepiaFilter(0.f);
case FilterOperation::SATURATE:
return FilterOperation::CreateSaturateFilter(1.f);
case FilterOperation::HUE_ROTATE:
return FilterOperation::CreateHueRotateFilter(0.f);
case FilterOperation::INVERT:
return FilterOperation::CreateInvertFilter(0.f);
case FilterOperation::BRIGHTNESS:
return FilterOperation::CreateBrightnessFilter(1.f);
case FilterOperation::CONTRAST:
return FilterOperation::CreateContrastFilter(1.f);
case FilterOperation::OPACITY:
return FilterOperation::CreateOpacityFilter(1.f);
case FilterOperation::BLUR:
return FilterOperation::CreateBlurFilter(0.f);
case FilterOperation::DROP_SHADOW:
return FilterOperation::CreateDropShadowFilter(
gfx::Point(0, 0), 0.f, SK_ColorTRANSPARENT);
case FilterOperation::COLOR_MATRIX: {
SkScalar matrix[20];
memset(matrix, 0, 20 * sizeof(SkScalar));
matrix[0] = matrix[6] = matrix[12] = matrix[18] = 1.f;
return FilterOperation::CreateColorMatrixFilter(matrix);
}
case FilterOperation::ZOOM:
return FilterOperation::CreateZoomFilter(1.f, 0);
case FilterOperation::SATURATING_BRIGHTNESS:
return FilterOperation::CreateSaturatingBrightnessFilter(0.f);
default:
NOTREACHED();
return FilterOperation::CreateEmptyFilter();
}
}
static float ClampAmountForFilterType(float amount,
FilterOperation::FilterType type) {
switch (type) {
case FilterOperation::GRAYSCALE:
case FilterOperation::SEPIA:
case FilterOperation::INVERT:
case FilterOperation::OPACITY:
return MathUtil::ClampToRange(amount, 0.f, 1.f);
case FilterOperation::SATURATE:
case FilterOperation::BRIGHTNESS:
case FilterOperation::CONTRAST:
case FilterOperation::BLUR:
case FilterOperation::DROP_SHADOW:
return std::max(amount, 0.f);
case FilterOperation::ZOOM:
return std::max(amount, 1.f);
case FilterOperation::HUE_ROTATE:
case FilterOperation::SATURATING_BRIGHTNESS:
return amount;
case FilterOperation::COLOR_MATRIX:
default:
NOTREACHED();
return amount;
}
}
// static
FilterOperation FilterOperation::Blend(const FilterOperation* from,
const FilterOperation* to,
double progress) {
FilterOperation blended_filter = FilterOperation::CreateEmptyFilter();
if (!from && !to)
return blended_filter;
const FilterOperation& from_op = from ? *from : CreateNoOpFilter(to->type());
const FilterOperation& to_op = to ? *to : CreateNoOpFilter(from->type());
if (from_op.type() != to_op.type())
return blended_filter;
DCHECK(to_op.type() != FilterOperation::COLOR_MATRIX);
blended_filter.set_type(to_op.type());
blended_filter.set_amount(ClampAmountForFilterType(
BlendFloats(from_op.amount(), to_op.amount(), progress), to_op.type()));
if (to_op.type() == FilterOperation::DROP_SHADOW) {
gfx::Point blended_offset(BlendInts(from_op.drop_shadow_offset().x(),
to_op.drop_shadow_offset().x(),
progress),
BlendInts(from_op.drop_shadow_offset().y(),
to_op.drop_shadow_offset().y(),
progress));
blended_filter.set_drop_shadow_offset(blended_offset);
blended_filter.set_drop_shadow_color(BlendSkColors(
from_op.drop_shadow_color(), to_op.drop_shadow_color(), progress));
} else if (to_op.type() == FilterOperation::ZOOM) {
blended_filter.set_zoom_inset(std::max(
BlendInts(from_op.zoom_inset(), to_op.zoom_inset(), progress), 0));
}
return blended_filter;
}
scoped_ptr<base::Value> FilterOperation::AsValue() const {
scoped_ptr<base::DictionaryValue> value(new DictionaryValue);
value->SetInteger("type", type_);
switch (type_) {
case FilterOperation::GRAYSCALE:
case FilterOperation::SEPIA:
case FilterOperation::SATURATE:
case FilterOperation::HUE_ROTATE:
case FilterOperation::INVERT:
case FilterOperation::BRIGHTNESS:
case FilterOperation::CONTRAST:
case FilterOperation::OPACITY:
case FilterOperation::BLUR:
case FilterOperation::SATURATING_BRIGHTNESS:
value->SetDouble("amount", amount_);
break;
case FilterOperation::DROP_SHADOW:
value->SetDouble("std_deviation", amount_);
value->Set("offset", MathUtil::AsValue(drop_shadow_offset_).release());
value->SetInteger("color", drop_shadow_color_);
break;
case FilterOperation::COLOR_MATRIX: {
scoped_ptr<ListValue> matrix(new ListValue);
for (size_t i = 0; i < arraysize(matrix_); ++i)
matrix->AppendDouble(matrix_[i]);
value->Set("matrix", matrix.release());
break;
}
case FilterOperation::ZOOM:
value->SetDouble("amount", amount_);
value->SetDouble("inset", zoom_inset_);
break;
}
return value.PassAs<base::Value>();
}
} // namespace cc