blob: f0cca86f744548c58b57bf238c1d0dccb37c27f9 [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 "cc/output/filter_operations.h"
#include <stddef.h>
#include <cmath>
#include <numeric>
#include "base/trace_event/trace_event_argument.h"
#include "base/values.h"
#include "cc/output/filter_operation.h"
#include "ui/gfx/geometry/rect.h"
namespace cc {
FilterOperations::FilterOperations() {}
FilterOperations::FilterOperations(const FilterOperations& other)
: operations_(other.operations_) {}
FilterOperations::FilterOperations(std::vector<FilterOperation>&& operations)
: operations_(std::move(operations)) {}
FilterOperations::~FilterOperations() {}
FilterOperations& FilterOperations::operator=(const FilterOperations& other) {
operations_ = other.operations_;
return *this;
}
FilterOperations& FilterOperations::operator=(FilterOperations&& other) {
operations_ = std::move(other.operations_);
return *this;
}
bool FilterOperations::operator==(const FilterOperations& other) const {
if (other.size() != size())
return false;
for (size_t i = 0; i < size(); ++i) {
if (other.at(i) != at(i))
return false;
}
return true;
}
void FilterOperations::Append(const FilterOperation& filter) {
operations_.push_back(filter);
}
void FilterOperations::Clear() {
operations_.clear();
}
bool FilterOperations::IsEmpty() const {
return operations_.empty();
}
static int SpreadForStdDeviation(float std_deviation) {
// Corresponds to MapStdDeviation in filter_operation.cc.
return std_deviation * 3;
}
gfx::Rect FilterOperations::MapRect(const gfx::Rect& rect,
const SkMatrix& matrix) const {
auto accumulate_rect = [matrix](const gfx::Rect& rect,
const FilterOperation& op) {
return op.MapRect(rect, matrix);
};
return std::accumulate(operations_.begin(), operations_.end(), rect,
accumulate_rect);
}
gfx::Rect FilterOperations::MapRectReverse(const gfx::Rect& rect,
const SkMatrix& matrix) const {
auto accumulate_rect = [&matrix](const gfx::Rect& rect,
const FilterOperation& op) {
return op.MapRectReverse(rect, matrix);
};
return std::accumulate(operations_.rbegin(), operations_.rend(), rect,
accumulate_rect);
}
void FilterOperations::GetOutsets(int* top,
int* right,
int* bottom,
int* left) const {
*top = *right = *bottom = *left = 0;
for (size_t i = 0; i < operations_.size(); ++i) {
const FilterOperation& op = operations_[i];
// TODO(hendrikw): We should refactor some of this. See crbug.com/523534.
if (op.type() == FilterOperation::REFERENCE) {
if (!op.image_filter())
continue;
SkIRect src = SkIRect::MakeWH(0, 0);
SkIRect dst = op.image_filter()->filterBounds(src, SkMatrix::I());
*top += std::max(0, -dst.top());
*right += std::max(0, dst.right());
*bottom += std::max(0, dst.bottom());
*left += std::max(0, -dst.left());
} else {
if (op.type() == FilterOperation::BLUR ||
op.type() == FilterOperation::DROP_SHADOW) {
int spread = SpreadForStdDeviation(op.amount());
if (op.type() == FilterOperation::BLUR) {
*top += spread;
*right += spread;
*bottom += spread;
*left += spread;
} else {
*top += std::max(0, spread - op.drop_shadow_offset().y());
*right += std::max(0, spread + op.drop_shadow_offset().x());
*bottom += std::max(0, spread + op.drop_shadow_offset().y());
*left += std::max(0, spread - op.drop_shadow_offset().x());
}
}
}
}
}
bool FilterOperations::HasFilterThatMovesPixels() const {
for (size_t i = 0; i < operations_.size(); ++i) {
const FilterOperation& op = operations_[i];
switch (op.type()) {
case FilterOperation::BLUR:
case FilterOperation::DROP_SHADOW:
case FilterOperation::ZOOM:
return true;
case FilterOperation::REFERENCE:
// TODO(hendrikw): SkImageFilter needs a function that tells us if the
// filter can move pixels. See crbug.com/523538.
return true;
case FilterOperation::OPACITY:
case FilterOperation::COLOR_MATRIX:
case FilterOperation::GRAYSCALE:
case FilterOperation::SEPIA:
case FilterOperation::SATURATE:
case FilterOperation::HUE_ROTATE:
case FilterOperation::INVERT:
case FilterOperation::BRIGHTNESS:
case FilterOperation::CONTRAST:
case FilterOperation::SATURATING_BRIGHTNESS:
case FilterOperation::ALPHA_THRESHOLD:
break;
}
}
return false;
}
bool FilterOperations::HasFilterThatAffectsOpacity() const {
for (size_t i = 0; i < operations_.size(); ++i) {
const FilterOperation& op = operations_[i];
// TODO(ajuma): Make this smarter for reference filters. Once SkImageFilter
// can report affectsOpacity(), call that.
switch (op.type()) {
case FilterOperation::OPACITY:
case FilterOperation::BLUR:
case FilterOperation::DROP_SHADOW:
case FilterOperation::ZOOM:
case FilterOperation::REFERENCE:
case FilterOperation::ALPHA_THRESHOLD:
return true;
case FilterOperation::COLOR_MATRIX: {
const SkScalar* matrix = op.matrix();
if (matrix[15] ||
matrix[16] ||
matrix[17] ||
matrix[18] != 1 ||
matrix[19])
return true;
break;
}
case FilterOperation::GRAYSCALE:
case FilterOperation::SEPIA:
case FilterOperation::SATURATE:
case FilterOperation::HUE_ROTATE:
case FilterOperation::INVERT:
case FilterOperation::BRIGHTNESS:
case FilterOperation::CONTRAST:
case FilterOperation::SATURATING_BRIGHTNESS:
break;
}
}
return false;
}
bool FilterOperations::HasReferenceFilter() const {
for (size_t i = 0; i < operations_.size(); ++i) {
if (operations_[i].type() == FilterOperation::REFERENCE)
return true;
}
return false;
}
FilterOperations FilterOperations::Blend(const FilterOperations& from,
double progress) const {
if (HasReferenceFilter() || from.HasReferenceFilter())
return *this;
bool from_is_longer = from.size() > size();
size_t shorter_size, longer_size;
if (size() == from.size()) {
shorter_size = longer_size = size();
} else if (from_is_longer) {
longer_size = from.size();
shorter_size = size();
} else {
longer_size = size();
shorter_size = from.size();
}
for (size_t i = 0; i < shorter_size; i++) {
if (from.at(i).type() != at(i).type())
return *this;
}
FilterOperations blended_filters;
for (size_t i = 0; i < shorter_size; i++) {
blended_filters.Append(
FilterOperation::Blend(&from.at(i), &at(i), progress));
}
if (from_is_longer) {
for (size_t i = shorter_size; i < longer_size; i++) {
blended_filters.Append(
FilterOperation::Blend(&from.at(i), NULL, progress));
}
} else {
for (size_t i = shorter_size; i < longer_size; i++)
blended_filters.Append(FilterOperation::Blend(NULL, &at(i), progress));
}
return blended_filters;
}
void FilterOperations::AsValueInto(
base::trace_event::TracedValue* value) const {
for (size_t i = 0; i < operations_.size(); ++i) {
value->BeginDictionary();
operations_[i].AsValueInto(value);
value->EndDictionary();
}
}
} // namespace cc