|  | // 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 <cmath> | 
|  |  | 
|  | #include "base/trace_event/trace_event_argument.h" | 
|  | #include "base/values.h" | 
|  | #include "cc/output/filter_operation.h" | 
|  |  | 
|  | namespace cc { | 
|  |  | 
|  | FilterOperations::FilterOperations() {} | 
|  |  | 
|  | FilterOperations::FilterOperations(const FilterOperations& other) | 
|  | : operations_(other.operations_) {} | 
|  |  | 
|  | FilterOperations::~FilterOperations() {} | 
|  |  | 
|  | FilterOperations& FilterOperations::operator=(const FilterOperations& other) { | 
|  | operations_ = 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) { | 
|  | // https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html#feGaussianBlurElement | 
|  | // provides this approximation for evaluating a gaussian blur by a triple box | 
|  | // filter. | 
|  | float d = floorf(std_deviation * 3.f * sqrt(8.f * atan(1.f)) / 4.f + 0.5f); | 
|  | return static_cast<int>(ceilf(d * 3.f / 2.f)); | 
|  | } | 
|  |  | 
|  | 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(ajuma): Add support for reference filters once SkImageFilter | 
|  | // reports its outsets. | 
|  | DCHECK(op.type() != FilterOperation::REFERENCE); | 
|  | 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 += spread - op.drop_shadow_offset().y(); | 
|  | *right += spread + op.drop_shadow_offset().x(); | 
|  | *bottom += spread + op.drop_shadow_offset().y(); | 
|  | *left += spread - op.drop_shadow_offset().x(); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | bool FilterOperations::HasFilterThatMovesPixels() const { | 
|  | for (size_t i = 0; i < operations_.size(); ++i) { | 
|  | const FilterOperation& op = operations_[i]; | 
|  | // TODO(ajuma): Once SkImageFilter reports its outsets, use those here to | 
|  | // determine whether a reference filter really moves pixels. | 
|  | switch (op.type()) { | 
|  | case FilterOperation::BLUR: | 
|  | case FilterOperation::DROP_SHADOW: | 
|  | case FilterOperation::ZOOM: | 
|  | case FilterOperation::REFERENCE: | 
|  | 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 |