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