Home | History | Annotate | Download | only in output
      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 
     14 FilterOperations::FilterOperations() {}
     15 
     16 FilterOperations::FilterOperations(const FilterOperations& other)
     17     : operations_(other.operations_) {}
     18 
     19 FilterOperations::~FilterOperations() {}
     20 
     21 FilterOperations& FilterOperations::operator=(const FilterOperations& other) {
     22   operations_ = other.operations_;
     23   return *this;
     24 }
     25 
     26 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 
     36 void FilterOperations::Append(const FilterOperation& filter) {
     37   operations_.push_back(filter);
     38 }
     39 
     40 void FilterOperations::Clear() {
     41   operations_.clear();
     42 }
     43 
     44 bool FilterOperations::IsEmpty() const {
     45   return operations_.empty();
     46 }
     47 
     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 
     56 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 
     84 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       case FilterOperation::ALPHA_THRESHOLD:
    106         break;
    107     }
    108   }
    109   return false;
    110 }
    111 
    112 bool 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 
    149 bool 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 
    157 FilterOperations 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 
    199 scoped_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