1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "cc/output/filter_operations.h"
6
7#include <cmath>
8
9#include "base/debug/trace_event_argument.h"
10#include "base/values.h"
11#include "cc/output/filter_operation.h"
12
13namespace cc {
14
15FilterOperations::FilterOperations() {}
16
17FilterOperations::FilterOperations(const FilterOperations& other)
18    : operations_(other.operations_) {}
19
20FilterOperations::~FilterOperations() {}
21
22FilterOperations& FilterOperations::operator=(const FilterOperations& other) {
23  operations_ = other.operations_;
24  return *this;
25}
26
27bool FilterOperations::operator==(const FilterOperations& other) const {
28  if (other.size() != size())
29    return false;
30  for (size_t i = 0; i < size(); ++i) {
31    if (other.at(i) != at(i))
32      return false;
33  }
34  return true;
35}
36
37void FilterOperations::Append(const FilterOperation& filter) {
38  operations_.push_back(filter);
39}
40
41void FilterOperations::Clear() {
42  operations_.clear();
43}
44
45bool FilterOperations::IsEmpty() const {
46  return operations_.empty();
47}
48
49static int SpreadForStdDeviation(float std_deviation) {
50  // https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html#feGaussianBlurElement
51  // provides this approximation for evaluating a gaussian blur by a triple box
52  // filter.
53  float d = floorf(std_deviation * 3.f * sqrt(8.f * atan(1.f)) / 4.f + 0.5f);
54  return static_cast<int>(ceilf(d * 3.f / 2.f));
55}
56
57void FilterOperations::GetOutsets(int* top,
58                                  int* right,
59                                  int* bottom,
60                                  int* left) const {
61  *top = *right = *bottom = *left = 0;
62  for (size_t i = 0; i < operations_.size(); ++i) {
63    const FilterOperation& op = operations_[i];
64    // TODO(ajuma): Add support for reference filters once SkImageFilter
65    // reports its outsets.
66    DCHECK(op.type() != FilterOperation::REFERENCE);
67    if (op.type() == FilterOperation::BLUR ||
68        op.type() == FilterOperation::DROP_SHADOW) {
69      int spread = SpreadForStdDeviation(op.amount());
70      if (op.type() == FilterOperation::BLUR) {
71        *top += spread;
72        *right += spread;
73        *bottom += spread;
74        *left += spread;
75      } else {
76        *top += spread - op.drop_shadow_offset().y();
77        *right += spread + op.drop_shadow_offset().x();
78        *bottom += spread + op.drop_shadow_offset().y();
79        *left += spread - op.drop_shadow_offset().x();
80      }
81    }
82  }
83}
84
85bool FilterOperations::HasFilterThatMovesPixels() const {
86  for (size_t i = 0; i < operations_.size(); ++i) {
87    const FilterOperation& op = operations_[i];
88    // TODO(ajuma): Once SkImageFilter reports its outsets, use those here to
89    // determine whether a reference filter really moves pixels.
90    switch (op.type()) {
91      case FilterOperation::BLUR:
92      case FilterOperation::DROP_SHADOW:
93      case FilterOperation::ZOOM:
94      case FilterOperation::REFERENCE:
95        return true;
96      case FilterOperation::OPACITY:
97      case FilterOperation::COLOR_MATRIX:
98      case FilterOperation::GRAYSCALE:
99      case FilterOperation::SEPIA:
100      case FilterOperation::SATURATE:
101      case FilterOperation::HUE_ROTATE:
102      case FilterOperation::INVERT:
103      case FilterOperation::BRIGHTNESS:
104      case FilterOperation::CONTRAST:
105      case FilterOperation::SATURATING_BRIGHTNESS:
106      case FilterOperation::ALPHA_THRESHOLD:
107        break;
108    }
109  }
110  return false;
111}
112
113bool FilterOperations::HasFilterThatAffectsOpacity() const {
114  for (size_t i = 0; i < operations_.size(); ++i) {
115    const FilterOperation& op = operations_[i];
116    // TODO(ajuma): Make this smarter for reference filters. Once SkImageFilter
117    // can report affectsOpacity(), call that.
118    switch (op.type()) {
119      case FilterOperation::OPACITY:
120      case FilterOperation::BLUR:
121      case FilterOperation::DROP_SHADOW:
122      case FilterOperation::ZOOM:
123      case FilterOperation::REFERENCE:
124      case FilterOperation::ALPHA_THRESHOLD:
125        return true;
126      case FilterOperation::COLOR_MATRIX: {
127        const SkScalar* matrix = op.matrix();
128        if (matrix[15] ||
129            matrix[16] ||
130            matrix[17] ||
131            matrix[18] != 1 ||
132            matrix[19])
133          return true;
134        break;
135      }
136      case FilterOperation::GRAYSCALE:
137      case FilterOperation::SEPIA:
138      case FilterOperation::SATURATE:
139      case FilterOperation::HUE_ROTATE:
140      case FilterOperation::INVERT:
141      case FilterOperation::BRIGHTNESS:
142      case FilterOperation::CONTRAST:
143      case FilterOperation::SATURATING_BRIGHTNESS:
144        break;
145    }
146  }
147  return false;
148}
149
150bool FilterOperations::HasReferenceFilter() const {
151  for (size_t i = 0; i < operations_.size(); ++i) {
152    if (operations_[i].type() == FilterOperation::REFERENCE)
153      return true;
154  }
155  return false;
156}
157
158FilterOperations FilterOperations::Blend(const FilterOperations& from,
159                                         double progress) const {
160  if (HasReferenceFilter() || from.HasReferenceFilter())
161    return *this;
162
163  bool from_is_longer = from.size() > size();
164
165  size_t shorter_size, longer_size;
166  if (size() == from.size()) {
167    shorter_size = longer_size = size();
168  } else if  (from_is_longer) {
169    longer_size = from.size();
170    shorter_size = size();
171  } else {
172    longer_size = size();
173    shorter_size = from.size();
174  }
175
176  for (size_t i = 0; i < shorter_size; i++) {
177    if (from.at(i).type() != at(i).type())
178      return *this;
179  }
180
181  FilterOperations blended_filters;
182  for (size_t i = 0; i < shorter_size; i++) {
183    blended_filters.Append(
184        FilterOperation::Blend(&from.at(i), &at(i), progress));
185  }
186
187  if (from_is_longer) {
188    for (size_t i = shorter_size; i < longer_size; i++) {
189      blended_filters.Append(
190          FilterOperation::Blend(&from.at(i), NULL, progress));
191    }
192  } else {
193    for (size_t i = shorter_size; i < longer_size; i++)
194      blended_filters.Append(FilterOperation::Blend(NULL, &at(i), progress));
195  }
196
197  return blended_filters;
198}
199
200void FilterOperations::AsValueInto(base::debug::TracedValue* value) const {
201  for (size_t i = 0; i < operations_.size(); ++i) {
202    value->BeginDictionary();
203    operations_[i].AsValueInto(value);
204    value->EndDictionary();
205  }
206}
207
208}  // namespace cc
209