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
12 namespace cc {
13
FilterOperations()14 FilterOperations::FilterOperations() {}
15
FilterOperations(const FilterOperations & other)16 FilterOperations::FilterOperations(const FilterOperations& other)
17 : operations_(other.operations_) {}
18
~FilterOperations()19 FilterOperations::~FilterOperations() {}
20
operator =(const FilterOperations & other)21 FilterOperations& FilterOperations::operator=(const FilterOperations& other) {
22 operations_ = other.operations_;
23 return *this;
24 }
25
operator ==(const FilterOperations & other) const26 bool 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
Append(const FilterOperation & filter)36 void FilterOperations::Append(const FilterOperation& filter) {
37 operations_.push_back(filter);
38 }
39
Clear()40 void FilterOperations::Clear() {
41 operations_.clear();
42 }
43
IsEmpty() const44 bool FilterOperations::IsEmpty() const {
45 return operations_.empty();
46 }
47
SpreadForStdDeviation(float std_deviation)48 static 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
GetOutsets(int * top,int * right,int * bottom,int * left) const56 void 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
HasFilterThatMovesPixels() const84 bool 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 break;
106 }
107 }
108 return false;
109 }
110
HasFilterThatAffectsOpacity() const111 bool FilterOperations::HasFilterThatAffectsOpacity() const {
112 for (size_t i = 0; i < operations_.size(); ++i) {
113 const FilterOperation& op = operations_[i];
114 // TODO(ajuma): Make this smarter for reference filters. Once SkImageFilter
115 // can report affectsOpacity(), call that.
116 switch (op.type()) {
117 case FilterOperation::OPACITY:
118 case FilterOperation::BLUR:
119 case FilterOperation::DROP_SHADOW:
120 case FilterOperation::ZOOM:
121 case FilterOperation::REFERENCE:
122 return true;
123 case FilterOperation::COLOR_MATRIX: {
124 const SkScalar* matrix = op.matrix();
125 if (matrix[15] ||
126 matrix[16] ||
127 matrix[17] ||
128 matrix[18] != 1 ||
129 matrix[19])
130 return true;
131 break;
132 }
133 case FilterOperation::GRAYSCALE:
134 case FilterOperation::SEPIA:
135 case FilterOperation::SATURATE:
136 case FilterOperation::HUE_ROTATE:
137 case FilterOperation::INVERT:
138 case FilterOperation::BRIGHTNESS:
139 case FilterOperation::CONTRAST:
140 case FilterOperation::SATURATING_BRIGHTNESS:
141 break;
142 }
143 }
144 return false;
145 }
146
HasReferenceFilter() const147 bool FilterOperations::HasReferenceFilter() const {
148 for (size_t i = 0; i < operations_.size(); ++i) {
149 if (operations_[i].type() == FilterOperation::REFERENCE)
150 return true;
151 }
152 return false;
153 }
154
Blend(const FilterOperations & from,double progress) const155 FilterOperations FilterOperations::Blend(const FilterOperations& from,
156 double progress) const {
157 if (HasReferenceFilter() || from.HasReferenceFilter())
158 return *this;
159
160 bool from_is_longer = from.size() > size();
161
162 size_t shorter_size, longer_size;
163 if (size() == from.size()) {
164 shorter_size = longer_size = size();
165 } else if (from_is_longer) {
166 longer_size = from.size();
167 shorter_size = size();
168 } else {
169 longer_size = size();
170 shorter_size = from.size();
171 }
172
173 for (size_t i = 0; i < shorter_size; i++) {
174 if (from.at(i).type() != at(i).type())
175 return *this;
176 }
177
178 FilterOperations blended_filters;
179 for (size_t i = 0; i < shorter_size; i++) {
180 blended_filters.Append(
181 FilterOperation::Blend(&from.at(i), &at(i), progress));
182 }
183
184 if (from_is_longer) {
185 for (size_t i = shorter_size; i < longer_size; i++) {
186 blended_filters.Append(
187 FilterOperation::Blend(&from.at(i), NULL, progress));
188 }
189 } else {
190 for (size_t i = shorter_size; i < longer_size; i++)
191 blended_filters.Append(FilterOperation::Blend(NULL, &at(i), progress));
192 }
193
194 return blended_filters;
195 }
196
AsValue() const197 scoped_ptr<base::Value> FilterOperations::AsValue() const {
198 scoped_ptr<base::ListValue> value(new ListValue);
199 for (size_t i = 0; i < operations_.size(); ++i)
200 value->Append(operations_[i].AsValue().release());
201 return value.PassAs<base::Value>();
202 }
203
204 } // namespace cc
205