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 <algorithm>
6
7#include "base/debug/trace_event_argument.h"
8#include "base/values.h"
9#include "cc/base/math_util.h"
10#include "cc/output/filter_operation.h"
11#include "ui/gfx/animation/tween.h"
12
13namespace cc {
14
15bool FilterOperation::operator==(const FilterOperation& other) const {
16  if (type_ != other.type_)
17    return false;
18  if (type_ == COLOR_MATRIX)
19    return !memcmp(matrix_, other.matrix_, sizeof(matrix_));
20  if (type_ == DROP_SHADOW) {
21    return amount_ == other.amount_ &&
22           drop_shadow_offset_ == other.drop_shadow_offset_ &&
23           drop_shadow_color_ == other.drop_shadow_color_;
24  }
25  if (type_ == REFERENCE)
26    return image_filter_.get() == other.image_filter_.get();
27  if (type_ == ALPHA_THRESHOLD) {
28    return region_ == other.region_ &&
29        amount_ == other.amount_ &&
30        outer_threshold_ == other.outer_threshold_;
31  }
32  return amount_ == other.amount_;
33}
34
35FilterOperation::FilterOperation(FilterType type, float amount)
36    : type_(type),
37      amount_(amount),
38      outer_threshold_(0),
39      drop_shadow_offset_(0, 0),
40      drop_shadow_color_(0),
41      zoom_inset_(0) {
42  DCHECK_NE(type_, DROP_SHADOW);
43  DCHECK_NE(type_, COLOR_MATRIX);
44  DCHECK_NE(type_, REFERENCE);
45  memset(matrix_, 0, sizeof(matrix_));
46}
47
48FilterOperation::FilterOperation(FilterType type,
49                                 const gfx::Point& offset,
50                                 float stdDeviation,
51                                 SkColor color)
52    : type_(type),
53      amount_(stdDeviation),
54      outer_threshold_(0),
55      drop_shadow_offset_(offset),
56      drop_shadow_color_(color),
57      zoom_inset_(0) {
58  DCHECK_EQ(type_, DROP_SHADOW);
59  memset(matrix_, 0, sizeof(matrix_));
60}
61
62FilterOperation::FilterOperation(FilterType type, SkScalar matrix[20])
63    : type_(type),
64      amount_(0),
65      outer_threshold_(0),
66      drop_shadow_offset_(0, 0),
67      drop_shadow_color_(0),
68      zoom_inset_(0) {
69  DCHECK_EQ(type_, COLOR_MATRIX);
70  memcpy(matrix_, matrix, sizeof(matrix_));
71}
72
73FilterOperation::FilterOperation(FilterType type, float amount, int inset)
74    : type_(type),
75      amount_(amount),
76      outer_threshold_(0),
77      drop_shadow_offset_(0, 0),
78      drop_shadow_color_(0),
79      zoom_inset_(inset) {
80  DCHECK_EQ(type_, ZOOM);
81  memset(matrix_, 0, sizeof(matrix_));
82}
83
84FilterOperation::FilterOperation(
85    FilterType type,
86    const skia::RefPtr<SkImageFilter>& image_filter)
87    : type_(type),
88      amount_(0),
89      outer_threshold_(0),
90      drop_shadow_offset_(0, 0),
91      drop_shadow_color_(0),
92      image_filter_(image_filter),
93      zoom_inset_(0) {
94  DCHECK_EQ(type_, REFERENCE);
95  memset(matrix_, 0, sizeof(matrix_));
96}
97
98FilterOperation::FilterOperation(FilterType type,
99                                 const SkRegion& region,
100                                 float inner_threshold,
101                                 float outer_threshold)
102    : type_(type),
103      amount_(inner_threshold),
104      outer_threshold_(outer_threshold),
105      drop_shadow_offset_(0, 0),
106      drop_shadow_color_(0),
107      zoom_inset_(0),
108      region_(region) {
109  DCHECK_EQ(type_, ALPHA_THRESHOLD);
110  memset(matrix_, 0, sizeof(matrix_));
111}
112
113FilterOperation::FilterOperation(const FilterOperation& other)
114    : type_(other.type_),
115      amount_(other.amount_),
116      outer_threshold_(other.outer_threshold_),
117      drop_shadow_offset_(other.drop_shadow_offset_),
118      drop_shadow_color_(other.drop_shadow_color_),
119      image_filter_(other.image_filter_),
120      zoom_inset_(other.zoom_inset_),
121      region_(other.region_) {
122  memcpy(matrix_, other.matrix_, sizeof(matrix_));
123}
124
125FilterOperation::~FilterOperation() {
126}
127
128static FilterOperation CreateNoOpFilter(FilterOperation::FilterType type) {
129  switch (type) {
130    case FilterOperation::GRAYSCALE:
131      return FilterOperation::CreateGrayscaleFilter(0.f);
132    case FilterOperation::SEPIA:
133      return FilterOperation::CreateSepiaFilter(0.f);
134    case FilterOperation::SATURATE:
135      return FilterOperation::CreateSaturateFilter(1.f);
136    case FilterOperation::HUE_ROTATE:
137      return FilterOperation::CreateHueRotateFilter(0.f);
138    case FilterOperation::INVERT:
139      return FilterOperation::CreateInvertFilter(0.f);
140    case FilterOperation::BRIGHTNESS:
141      return FilterOperation::CreateBrightnessFilter(1.f);
142    case FilterOperation::CONTRAST:
143      return FilterOperation::CreateContrastFilter(1.f);
144    case FilterOperation::OPACITY:
145      return FilterOperation::CreateOpacityFilter(1.f);
146    case FilterOperation::BLUR:
147      return FilterOperation::CreateBlurFilter(0.f);
148    case FilterOperation::DROP_SHADOW:
149      return FilterOperation::CreateDropShadowFilter(
150          gfx::Point(0, 0), 0.f, SK_ColorTRANSPARENT);
151    case FilterOperation::COLOR_MATRIX: {
152      SkScalar matrix[20];
153      memset(matrix, 0, 20 * sizeof(SkScalar));
154      matrix[0] = matrix[6] = matrix[12] = matrix[18] = 1.f;
155      return FilterOperation::CreateColorMatrixFilter(matrix);
156    }
157    case FilterOperation::ZOOM:
158      return FilterOperation::CreateZoomFilter(1.f, 0);
159    case FilterOperation::SATURATING_BRIGHTNESS:
160      return FilterOperation::CreateSaturatingBrightnessFilter(0.f);
161    case FilterOperation::REFERENCE:
162      return FilterOperation::CreateReferenceFilter(
163          skia::RefPtr<SkImageFilter>());
164    case FilterOperation::ALPHA_THRESHOLD:
165      return FilterOperation::CreateAlphaThresholdFilter(SkRegion(), 1.f, 0.f);
166  }
167  NOTREACHED();
168  return FilterOperation::CreateEmptyFilter();
169}
170
171static float ClampAmountForFilterType(float amount,
172                                      FilterOperation::FilterType type) {
173  switch (type) {
174    case FilterOperation::GRAYSCALE:
175    case FilterOperation::SEPIA:
176    case FilterOperation::INVERT:
177    case FilterOperation::OPACITY:
178    case FilterOperation::ALPHA_THRESHOLD:
179      return MathUtil::ClampToRange(amount, 0.f, 1.f);
180    case FilterOperation::SATURATE:
181    case FilterOperation::BRIGHTNESS:
182    case FilterOperation::CONTRAST:
183    case FilterOperation::BLUR:
184    case FilterOperation::DROP_SHADOW:
185      return std::max(amount, 0.f);
186    case FilterOperation::ZOOM:
187      return std::max(amount, 1.f);
188    case FilterOperation::HUE_ROTATE:
189    case FilterOperation::SATURATING_BRIGHTNESS:
190      return amount;
191    case FilterOperation::COLOR_MATRIX:
192    case FilterOperation::REFERENCE:
193      NOTREACHED();
194      return amount;
195  }
196  NOTREACHED();
197  return amount;
198}
199
200// static
201FilterOperation FilterOperation::Blend(const FilterOperation* from,
202                                       const FilterOperation* to,
203                                       double progress) {
204  FilterOperation blended_filter = FilterOperation::CreateEmptyFilter();
205
206  if (!from && !to)
207    return blended_filter;
208
209  const FilterOperation& from_op = from ? *from : CreateNoOpFilter(to->type());
210  const FilterOperation& to_op = to ? *to : CreateNoOpFilter(from->type());
211
212  if (from_op.type() != to_op.type())
213    return blended_filter;
214
215  DCHECK(to_op.type() != FilterOperation::COLOR_MATRIX);
216  blended_filter.set_type(to_op.type());
217
218  if (to_op.type() == FilterOperation::REFERENCE) {
219    if (progress > 0.5)
220      blended_filter.set_image_filter(to_op.image_filter());
221    else
222      blended_filter.set_image_filter(from_op.image_filter());
223    return blended_filter;
224  }
225
226  blended_filter.set_amount(ClampAmountForFilterType(
227      gfx::Tween::FloatValueBetween(progress, from_op.amount(), to_op.amount()),
228      to_op.type()));
229
230  if (to_op.type() == FilterOperation::DROP_SHADOW) {
231    gfx::Point blended_offset(
232        gfx::Tween::LinearIntValueBetween(progress,
233                                          from_op.drop_shadow_offset().x(),
234                                          to_op.drop_shadow_offset().x()),
235        gfx::Tween::LinearIntValueBetween(progress,
236                                          from_op.drop_shadow_offset().y(),
237                                          to_op.drop_shadow_offset().y()));
238    blended_filter.set_drop_shadow_offset(blended_offset);
239    blended_filter.set_drop_shadow_color(gfx::Tween::ColorValueBetween(
240        progress, from_op.drop_shadow_color(), to_op.drop_shadow_color()));
241  } else if (to_op.type() == FilterOperation::ZOOM) {
242    blended_filter.set_zoom_inset(
243        std::max(gfx::Tween::LinearIntValueBetween(
244                     from_op.zoom_inset(), to_op.zoom_inset(), progress),
245                 0));
246  } else if (to_op.type() == FilterOperation::ALPHA_THRESHOLD) {
247    blended_filter.set_outer_threshold(ClampAmountForFilterType(
248            gfx::Tween::FloatValueBetween(progress,
249                                          from_op.outer_threshold(),
250                                          to_op.outer_threshold()),
251            to_op.type()));
252    blended_filter.set_region(to_op.region());
253  }
254
255  return blended_filter;
256}
257
258void FilterOperation::AsValueInto(base::debug::TracedValue* value) const {
259  value->SetInteger("type", type_);
260  switch (type_) {
261    case FilterOperation::GRAYSCALE:
262    case FilterOperation::SEPIA:
263    case FilterOperation::SATURATE:
264    case FilterOperation::HUE_ROTATE:
265    case FilterOperation::INVERT:
266    case FilterOperation::BRIGHTNESS:
267    case FilterOperation::CONTRAST:
268    case FilterOperation::OPACITY:
269    case FilterOperation::BLUR:
270    case FilterOperation::SATURATING_BRIGHTNESS:
271      value->SetDouble("amount", amount_);
272      break;
273    case FilterOperation::DROP_SHADOW:
274      value->SetDouble("std_deviation", amount_);
275      value->BeginArray("offset");
276      MathUtil::AddToTracedValue(drop_shadow_offset_, value);
277      value->EndArray();
278      value->SetInteger("color", drop_shadow_color_);
279      break;
280    case FilterOperation::COLOR_MATRIX: {
281      value->BeginArray("matrix");
282      for (size_t i = 0; i < arraysize(matrix_); ++i)
283        value->AppendDouble(matrix_[i]);
284      value->EndArray();
285      break;
286    }
287    case FilterOperation::ZOOM:
288      value->SetDouble("amount", amount_);
289      value->SetDouble("inset", zoom_inset_);
290      break;
291    case FilterOperation::REFERENCE: {
292      int count_inputs = 0;
293      bool can_filter_image_gpu = false;
294      if (image_filter_) {
295        count_inputs = image_filter_->countInputs();
296        can_filter_image_gpu = image_filter_->canFilterImageGPU();
297      }
298      value->SetBoolean("is_null", !image_filter_);
299      value->SetInteger("count_inputs", count_inputs);
300      value->SetBoolean("can_filter_image_gpu", can_filter_image_gpu);
301      break;
302    }
303    case FilterOperation::ALPHA_THRESHOLD: {
304        value->SetDouble("inner_threshold", amount_);
305        value->SetDouble("outer_threshold", outer_threshold_);
306        scoped_ptr<base::ListValue> region_value(new base::ListValue());
307        value->BeginArray("region");
308        for (SkRegion::Iterator it(region_); !it.done(); it.next()) {
309          value->AppendInteger(it.rect().x());
310          value->AppendInteger(it.rect().y());
311          value->AppendInteger(it.rect().width());
312          value->AppendInteger(it.rect().height());
313        }
314        value->EndArray();
315      }
316      break;
317  }
318}
319
320}  // namespace cc
321